Open · self-hostable · conformance-first
Every AI assistant acts as you. Cistern makes it ask.
One gatekeeper between every caller and the data. Decided fresh on every request, never cached, with the deciding rule named in a receipt.
Cistern is an open server that keeps a person's data where they decide, and makes every application and AI assistant ask permission instead of taking copies. The owner writes the rule (this assistant, this folder) and the server enforces it on every request, revokes it on the very next one, and leaves a receipt of every allow and deny. Built on the Solid specifications; assistants connect over MCP, applications over plain HTTP: one gatekeeper for both.
docker pull ghcr.io/enrichmeai/cistern:0.2.0
The refusal is the product
Today an assistant logs in as its user: whatever the person can see, it can see. One prompt injection has the authority of the whole person; revoking one assistant means revoking yourself; and afterwards nobody can say which agent read what. The protocols will not fix this. MCP defines how an assistant connects, not what the person allows it to touch.
Cistern puts one gatekeeper between every caller and the data. Who is asking, then the owner's permission file, then allow or refuse: decided fresh on every request, never cached, with the deciding rule named in a receipt. There is no privileged path around it. The MCP front door and plain HTTP cross the same filter, so an MCP tool call shows up in the receipts exactly like a curl.
or any app
MCP or plain HTTP. Same door, no privileged path.
asking
Its own principal, not the owner's login.
rule
Read at enforcement time, next to the data. Deny by default.
refuse
Receipted either way, with the rule that decided it.
The person writes the rule
This assistant, this folder. Not a developer's scope list, not an IT admin's console. Grants can only narrow the owner's own authority.
Deny by default
The rule lives next to the data and is read at enforcement time. An assistant outside its grant gets a refusal, not an empty folder.
No token to wait out
Delete the rule and the very next request is refused. No restart, no reissue, no cache to purge, because there is none.
Who read what, under which rule
Every allow and every deny is recorded with the rule that decided it, queryable by the owner. Audit is a query, not a forensic exercise.
Run it in five minutes
One container, three environment variables, and the server is enforcing. The CLI ships with the release, so no build is required.
# run enforcing: setting the owner turns Web Access Control ondocker run --rm -p 127.0.0.1:3737:3000 \-e CISTERN_OWNER_WEBID='https://you.example/profile#me' \-e CISTERN_OWNER_TOKEN="$(openssl rand -hex 32)" \-e CISTERN_BASE_URL='http://127.0.0.1:3737' \-v cistern-data:/data ghcr.io/enrichmeai/cistern:0.2.0# grant, and take it back: the CLI is a release assetjava -jar cistern-cli-0.2.0.jar grant <webid> --read /matters/2026-114/java -jar cistern-cli-0.2.0.jar revoke <webid> /matters/2026-114/
127.0.0.1, not localhost. On a Mac localhost resolves to ::1 first while Docker binds IPv4 only, so any other process on the port answers instead, with a 401 that looks exactly like this server misbehaving.
A WebID is a URL that names a person or an agent. For the command above, it is only a name, and nothing has to be published at it. Cistern writes it into the root permission file so the rules have somebody to belong to, and the owner token is what proves you are that somebody. Put your own domain in and carry on; the five minutes stay five minutes.
It starts to matter when a second party is involved. Once other people or agents sign in through an identity provider, their WebID has to resolve, because Cistern reads it to learn which provider is allowed to issue tokens for them. At that point you want a real one: an identity provider issues it, or you host the document yourself, and Cistern can seed one inside the pod for you when the WebID lives here rather than at a provider.
The Solid project lists providers who will issue you one, and the integration playbook covers wiring a provider to a pod.
docker compose up -d --build # Cistern + Keycloak, loopback only./seed.sh && ./sample-app/run.sh # two named apps: allowed inside the grant,# refused outside it, revoked, receipted: 15 checks
What a caller can reach
Two front doors, one filter. Everything below is subject to the same rule read and the same receipt.
| Surface | How a caller authenticates | What it can do | On refusal |
|---|---|---|---|
| MCP front door | A service principal of its own, issued per application | Seven tools over the granted paths, over stdio for a desktop client or Streamable HTTP for one on another machine | A structured refusal, not an empty result |
| Plain HTTP | Any OpenID Connect issuer, or a service principal | Read and write within Web Access Control | An HTTP refusal naming no more than it must |
| CLI | The owner token set at startup | Create pods, grant, revoke, read receipts | Not applicable: the owner holds the authority |
| Receipts | The owner, over the same filter | Query every allow and deny by rule, caller or path | Not applicable |
The authoritative reference is the shipped documentation: docs → and the integration playbook →.
What's real, stated plainly
This project has a rule: nothing is announced before it has run on real infrastructure. So here is the honest state, not the roadmap in disguise. Rows say whether they describe the released image or the current source.
- Release
ghcr.io/enrichmeai/cistern:0.2.0multi-arch image, jars, CLI, the MCP bridge and checksums live- Identity
- Logins from any OpenID Connect issuer, plus per-application service principals in v0.2.0 · Solid-OIDC token validation and DPoP proofs are on
main, merged after the 0.2.0 tag, so the image above predates them on main - Authority
- Web Access Control enforced on every request; grants and pods by one command; every decision receipted and queryable in v0.2.0
- Agents
- The MCP front door with seven tools, structured refusals and revocation on the very next call, and plain HTTP, behind one filter in v0.2.0
- Conformance
- Built against the official Solid conformance suite, and the score only moves forward. As of the run recorded on 28 August 2026 it reads 0 of 41 executed, and what changed is worth stating: the suite's client registration now passes, where before it could not. The run halts one step later, because the suite's own client sends DPoP proofs without the
athclaim that RFC 9449 requires a resource server to check, and Cistern checks it. We have offered the one-line fix upstream rather than working around it. Until an unmodified harness produces a number, this one stands at zero. - Proof
- Every release is held to a stranger test before this page names it, v0.2.0 included: pull, run, grant, refuse, revoke, receipt, from the published artifacts alone, following only the shipped docs. 1,987 green tests behind it.
- Licence
- Apache 2.0, free for any use including commercial. Support, integration and hosting are how the company earns.
The release gate, in one sentence"An agent could read exactly what it was granted and nothing else, and the owner took it back in the time it took to delete one line, with a receipt naming the rule. Releases here carry receipts. In this product, literally."
A release carrying Solid-OIDC
Token validation, DPoP proofs and WebID checks are merged and tested on main. The next tagged release is what puts them in an image you can pull.
Penstock as the reference client
An agent asking for a file as its own principal, refused outside its grant, receipted on the owner's side. See Penstock →
Community pods
An owner who can be more than one person, rules that survive a change of membership, and a record every member can read. Nothing shipped yet. The thinking →
Is this a replacement for my identity provider?
No. Cistern authenticates against any OpenID Connect issuer you already run, and issues per-application service principals on top. It decides what a caller may touch, not who your people are.
Does the assistant have to know about Cistern?
Only that it speaks MCP. Any assistant that does reaches the pod through the same enforcement as everything else, and gets a structured refusal when it asks outside its grant.
What does revocation actually cost?
One line deleted. There is no token lifetime to wait out and no cache to purge, because the rule is read at enforcement time on every request.
Do I need a WebID before I can start?
No. For the quickstart the WebID is only a name: nothing has to be published at that URL, and the owner token is what proves you are its owner. You need a WebID that resolves once other people or agents sign in through an identity provider, because Cistern reads the document to learn which provider may issue tokens for them.
Where does the data physically live?
Wherever you run the container. It is one image with a volume, on your hardware or your cloud, with your keys. Nothing phones home.
Why does the conformance score read zero?
The suite authenticates its test clients before it runs a single assertion, and that step now passes. It stops one step later on a detail of the DPoP proofs its client sends: RFC 9449 requires a resource server to reject a proof without the ath claim, and Cistern does. We have sent the fix upstream. The number stays on this page either way.
Why build on Solid at all?
Because the work was already done, in the open. A community agreed on how data ownership and access rules should work, wrote it down precisely enough to implement, published a suite that says when an implementation is wrong, and shipped a reference server. Cistern is a different implementation of that design, and it exists because those three things existed first.
Is any of it held back for a paid tier?
No, and there is not going to be one. All of it is Apache 2.0 and free for commercial use. Support, integration and hosting are how the company earns.
Can I put v0.2.0 on a network?
It has cleared the stranger test, so a stranger can pull, run, grant, refuse, revoke and read receipts from the published artifacts alone. Read the release notes for the limitations, which are stated plainly.