How it works
How does Inhold keep agent memory inside the customer's cloud?
Inhold is designed to install in the customer's own cloud account. Your agent talks to it over MCP or REST, and each policy decision is logged before data moves. Today it runs on one machine.
Inhold is being built. No customers yet. Repo goes public 9 Nov 2026.
The path of one request
From install to evidence pack
The customer installs Inhold in their account
Planned path: a Terraform module the customer's team runs in their own AWS account. Postgres 16 in private subnets, storage on their KMS key, agent sign-in checks through regional AWS STS and audit anchors in S3 and KMS, all through VPC endpoints, with no other internet egress. Images come from a registry they mirror. designed, in build
Today the v0 runs on one machine with SQLite and the synthetic hospital.
They write the policy bundle
A JSON document that names each agent, each person, the purposes each may use, and which data labels each purpose may read, write or delete. Their team owns it and can diff it. After the first boot, changes take effect only through the policy API, which logs them with the new hash.
Your agent connects
Over REST or MCP over HTTP on the gateway, both in v0. Local runs can also use MCP over stdio. The agent's identity is fixed when it authenticates, so nothing a model writes into a tool call can change who it is. Every call runs under a purpose: named in the REST request, or fixed for the session on MCP.
Inhold decides and logs
Writes are labelled locally (MRN, DOB, ICD-10 and more). Reads are checked on caller, purpose and labels. Missing and refused ids get the same answer, so an agent cannot probe for what exists. Each decision writes one audit row in the same transaction as the data.
The check runs in Inhold's code, outside the model, so an instruction hidden in a document cannot change what the agent is allowed to read. It can still misuse what it is allowed to read.
Outside research points the same way. In a small June 2026 study, arXiv 2606.26479, a policy check outside the model (Progent) cut attack success from 25.8% to 4.2%, and an adaptive attack written to beat it reached 2.6%. The study tested Progent, not Inhold.
People correct the agent
Corrections come only from people. Each is stored with a diff and turned into a draft skill. A named approver must approve it before any agent can use it.
The reviewer gets the evidence pack
Policy, audit rows, chain verification, the 164.312 control map and the answer bank, with a manifest of SHA-256 hashes. The reviewer can check each new pack's chain against an earlier pack they kept; the first pack has to be taken on trust.
Architecture
What sits where in the planned AWS install
Everything inside the outlined frame runs in the customer's account. The AWS install is in build; today v0 runs these parts on one machine with SQLite.
Integration
The same Memory methods, now async, through Inhold
Connect your agent to the MCP server. The server binds identity and purpose at session start; the model cannot change them in a tool call.
Trimmed from the integration demo. The complete before and after files go public with the repo on 9 Nov 2026.
# before: local memory, trimmed class Memory: def __init__(self): self._items = [] def add(self, text): self._items.append(text) # after: MCP client, trimmed # Identity and purpose bind when the MCP server starts. class Memory: def __init__(self, session): self.session = session async def add(self, text): await self.session.call_tool("context_write", {"kind": "memory", "body": text}) async def search(self, query): r = await self.session.call_tool("context_search", {"query": query}) # Response handling omitted here; see the full demo.
MCP
Which MCP tools does Inhold expose?
Inhold exposes four MCP tools. The purpose is fixed for the session before any tool runs, so no tool takes a purpose argument.
| Tool | Purpose required | What is logged |
|---|---|---|
context_search | The session's purpose. Labels outside it are left out before ranking. | One search row with the filter applied and the ids returned, then one read row per returned item. |
context_write | The session's purpose must allow writing the item's labels, including any detected on write. | One write row, allowed or denied. |
context_get | The session's purpose must allow reading the item's labels. Missing and refused ids get the same answer. | One read row, allowed or denied, with the real reason. |
skill_list | The session's purpose. Only approved skills for that purpose are returned. | One read row for each skill checked. |
From core/inhold/mcp_server.py and core/inhold/service.py, public from 9 Nov 2026.
The policy
What a bundle looks like
From the synthetic hospital in the repo. The billing agent can never read a note labelled PHI.
{
"purposes": {
"prior_auth": { "read": ["PHI", "PII", "internal", "public"], "write": ["PHI", "PII", "internal", "public"] },
"billing": { "read": ["PII", "internal", "public"], "write": ["PII", "internal"] }
},
"agents": {
"prior-auth-agent": { "vendor": "Acme Health AI", "purposes": ["prior_auth"] },
"billing-agent": { "vendor": "Acme Health AI", "purposes": ["billing"] }
},
"humans": {
"nurse.rivera": { "roles": ["clinician", "skill_approver"], "purposes": ["prior_auth"] },
"secops.okafor": { "roles": ["auditor"], "purposes": [] }
}
}
The data map moved: see where each kind of memory lives and who can read it.