Ziggurat

Models propose. Humans decide what persists.

Project
Ziggurat 0.1.0
Status
pre-release
Runtime
Node.js 22+
Licence
MIT
Package
source only, not on npm

Ziggurat is a human-gated memory firewall: a local TypeScript reference implementation that treats durable AI memory as a privileged write surface.

A model can read authorized content and draft a complete, evidence-backed candidate. It cannot admit that candidate to durable shared memory. Only the holder of an external Ed25519 private key that operator policy assigns to a reviewer can. Ziggurat ships no signer. Keeping the private key outside the vault and inaccessible to the model and Ziggurat process is an operator responsibility. The receipt proves key control and exact-content authorization, not humanity or attention.

From draft to authorized memory
  1. Model
    Propose a draft

    Content and source ranges, without admission authority.

  2. Host
    Validate and stage Silver

    Resolve citations and check the live evidence.

  3. Reviewer
    Author the page and sign externally

    Keep the private key separate from the model and host.

  4. Host
    Admit eligible Gold

    Verify the receipt and every other eligibility condition.

Gold MCP Read-only reference data

Figure 1. The review hand-off is external. No shipped path signs or approves a page.

Durable memory turns one poisoned document into a repeated write

Persistent context means a poisoned document, a fabricated preference, or an embedded instruction outlives the session that introduced it. One bad write keeps paying out, and the payout is silent: later sessions read it as ordinary background.

Blast radius of one poisoned writeTwo lanes of twelve session slots. In the ephemeral lane only the first slot is filled. In the durable-memory lane the first slot is filled and every later slot stays filled, continuing past the edge of the drawing.Ephemeral contextblast radius: one sessionDurable memoryblast radius: every later session
Figure 2. Persistence is what converts a single successful injection into a standing capability.

A reviewed_by field is a claim. A receipt is a capability.

Anything that can write the page can write the string. Review metadata records that someone says a review happened; it cannot demonstrate that a particular key holder approved these exact bytes. Ziggurat keeps the two as different kinds of object.

knowledge/topic.md

No authority
---
status: reviewed
reviewed_by: alice
retrieval_eligible: true
---

Self-asserted metadata changes nothing. Without a matching receipt the page stays out of Gold and out of the Gold index, whatever it says about itself.

authorizations/topic.md.authorization.json

Signed
{
  "schema_version": 1,
  "decision": "admit",
  "target_path": "knowledge/topic.md",
  "content_sha256": "<canonical page digest>",
  "reviewer_id": "committee-chair",
  "reviewed_at": "2026-08-29T21:00:00Z",
  "key_id": "committee-chair-2026",
  "algorithm": "ed25519",
  "signature": "<detached base64 signature>"
}

Verified against a public key configured in config/trust.yaml. It binds the decision, the target path, the canonical page digest, the reviewer, the timestamp, and the key. It is necessary for Gold, not sufficient on its own.

Follow a poisoned memo up the structure

Section through the Ziggurat admission structureA stepped section drawn from bottom to top. An untrusted capture enters at ground level and becomes a Bronze course of preserved evidence. A narrower Silver course of staged proposals sits above it. Above Silver the structure stops: a recessed gap separates it from the Gold course, and the only line crossing that gap enters horizontally from outside the plate, representing an external Ed25519 key. A block labelled reviewed_by colon alice sits inside the gap carrying a refused stamp. The Gold course sits above the gap, and a narrow aperture at the summit emits a single read-only arrow. The seven levels are annotated in the numbered list beside this figure.00untrusted captureingest01BRONZEpreservedrefine02SILVERproposedmodel reach ends03reviewed_by: aliceREFUSED04external keyoutside the vault05GOLDauthorized06read only
Figure 3. Read the structure from the bottom up. A model reaches Silver; Gold also requires external authorization and every eligibility check.
  1. Capture an untrusted memo

    The source starts as evidence, not an instruction or an approved claim.

  2. Preserve it as Bronze

    ingest records canonical text without overwriting an existing capture.

  3. Stage a Silver proposal

    The model drafts content and source ranges. The host checks them against live evidence.

  4. Refuse self-asserted authority

    A reviewed_by field alone does not admit the page.

  5. Review and sign outside the system

    The reviewer authors the final page and obtains an external Ed25519 signature.

  6. Verify before admitting Gold

    build requires the receipt and every other eligibility condition.

  7. Retrieve reference data only

    Gold MCP exposes read-only context, not instruction authority.

Three indexes, physically separate

build writes three separate version-2 indexes rather than one store with filters. Each carries its own profile literal, deterministic chunk IDs, a trust-policy fingerprint, and a corpus fingerprint over complete chunk integrity.

IndexContentsIntended useShipped MCP
gold-index.jsonAuthorized Gold onlyAnswer contextExposed, read only
review-index.jsonPolicy-safe Silver proposals plus authorized GoldLocal advisory reviewNot exposed
evidence-index.jsonPolicy-safe Bronze plus authorized GoldLocal forensic tracingNot exposed

What is enforced, and what is not

The second column is not a disclaimer. A boundary you cannot describe the edges of is a boundary you cannot rely on.

Enforced in code

  • ingest is the only Bronze writer, and captures are no-overwrite and body-hash verified; later mutation is detectable, not prevented.
  • An ingest source must resolve to a real regular file physically under inbox/.
  • refine can write only strict version-2 artifacts under .ziggurat/proposals/.
  • Silver candidates cannot carry status, reviewer, receipt, or admission metadata.
  • Every Silver citation must match exact Bronze bytes, hashes, and line ranges.
  • Gold requires a detached Ed25519 receipt from a key configured in config/trust.yaml.
  • gold, review, and evidence are physically separate version-2 indexes.
  • Shipped MCP startup is Gold-only and exposes exactly two read-only tools.
  • Model endpoints are HTTP loopback only, with redirects disabled, a 30 second deadline, and 1 MiB request and response ceilings.
  • Every returned chunk reports content_role: reference and instruction_authority: none.

Explicitly not guaranteed

  • Ziggurat is not an OS sandbox or a multi-tenant authorization service.
  • An attacker with arbitrary vault filesystem access can replace the trust policy, sources, receipts, and indexes, then rebuild.
  • A stolen reviewer key, a compromised reviewer, or an inattentive approval can authorize harmful or false content.
  • A valid signature proves control of a configured key and approval of exact content. It does not prove factual truth.
  • Approved text can still contain prompt injection; see provenance and authority.
  • Application-level path checks cannot fully close time-of-check to time-of-use windows.
  • No key custody, revocation service, hosted identity, transport security, or GUI review system is provided.

Three ways to check this yourself

Watch the boundary hold

The garden walkthrough runs the poisoned-memory scenario against fixture data and stops at the human signing boundary by design. Clone the repository, then from the checkout root:

npm ci
npm run build
node scripts/run-garden-walkthrough.mjs

Start a vault

Create the directories and an empty trust policy, capture one piece of evidence, and build the three indexes.

node dist/src/cli/main.js init --root ./my-vault
cp some-note.md ./my-vault/inbox/
node dist/src/cli/main.js ingest --root ./my-vault \
  --file inbox/some-note.md
node dist/src/cli/main.js build --root ./my-vault

Where this actually is

Ziggurat is distributed as source, not as a published npm package. Contracts, index formats, and CLI behaviour may change before 1.0.

Continuous integration is configured to run the full suite on Linux, macOS, and Windows against Node.js 22 and 24. Bounded model, retrieval, signing-interoperability, and operating-envelope measurements are documented in project status, alongside failures and remaining gaps. Human usability is unmeasured; no adoption, deployment, third-party audit, or production maturity is claimed.

What it is
pre-release, single-operator, source-distributed
What it is not
hosted service, OS sandbox, key custody