Technology
Built so nobody has to trust us.
This is the page for whoever reads the code. Every part of the stack is here, with the standards it uses and what each one gives you, so your engineers can check every claim on this site for themselves.
The standards
Five public standards, no private ones.
Each one is published, reviewed and already implemented in free tools, which is why a record checks without us.
- Ed25519
- The seal. A signature any free tool can check, so the person asking never needs our software to believe the file.
- SHA-256
- The fingerprint on every step. Each step carries the fingerprint of the one before it. Change a single character and every step after it stops matching.
- RFC 8785
- The same bytes, every time. Canonical JSON means one record always produces the same bytes, so two checkers on two continents get the same answer.
- RFC 3161
- An outside clock. On Team, a timestamp from an independent time authority countersigns the record, so its time is not ours to set.
- in-toto · DSSE
- An envelope auditors already open. A record can travel inside the envelope supply-chain tools already read. This support is provisional, and the record inside never changes.
01 · Before it runs · Portcullis
What was it allowed to do?
What it gives you. Portcullis reads all of it before the agent starts and shows you, line by line, what could reach your secrets or send your data out. Then it signs a receipt, so “we checked” becomes something you can show rather than something you say.
Portcullis reads the instruction layer as text: Claude Code plugins, Agent Skills, MCP tools and the prose inside them. It reports each signal with the exact file and line, then signs a vetting receipt over the bytes it read.
It never runs what it is scanning.
That is the whole design, and it is enforced rather than intended. The test suite runs every scan in a child process where child_process and dynamic loading of any target path throw before scanner code loads, and a companion preload makes every network call throw. Hostile fixtures still scan to completion, which proves the no-execute and no-network properties by behaviour instead of by grep.
- What it reads
- Claude Code plugins, Agent Skills and MCP tools: manifests, code, and the prose an agent will read as instructions
- How it reads
- Read-only, one file at a time, symlinks flagged and never followed, and it never leaves the folder you point it at
- What it looks for
- Instruction injection (INJ), exfiltration (EXF), dangerous capability (CAP), a manifest that says less than the code does (MAN, PKG)
- What you get
- A findings report citing the rule, the file and the line, and a signed vetting receipt
- In CI
--fail-on highturns it into a gate on every intake
Run it · from the portcullis repo
# scan a package portcullis ./some-plugin # gate a marketplace intake portcullis ./a-marketplace-checkout --fail-on high # re-check a receipt someone handed you portcullis --verify portcullis-out/receipt.json
02 · While it works · Logbook
What did it actually do?
What it gives you. Logbook turns the run into one sealed file: every step in order, each one locked to the last. Send it to whoever asks. They check it on their own computer, for free, without calling us.
Logbook writes the run as agent-work/record@0: every step in order, each carrying a SHA-256 fingerprint of the step before, sealed with Ed25519. The record is written by the harness running the agent, never by the agent, and a third party checks it offline.
Check one yourself, right now.
The home page runs a real verifier in your browser. It rebuilds the chain and tests the Ed25519 signature with WebCrypto, in your tab. Press Tamper With It and the check refuses, naming the step that changed. Go and break one.
- Format
agent-work/record@0, owned by the agent-work repo; Logbook extends it and never forks it- Chain
- Each step carries a fingerprint of its own contents and names the step before it. Alter one step and the check fails at exactly that step
- Signature
- Ed25519 over
canonical JSON (RFC 8785), tagged with its domain, so a record can never pass as some other product’s file - Contents
- Digests by default, bodies only when you opt in, so a record can leave your company without carrying your data or your code
- Checking it
- Exit 0 means the chain and the signature hold. No network, no dependencies
Run it · from the logbook repo
# build it npm install && npm run build # a development key, never for production node bin/logbook-keygen.mjs dev # exit 0: chain and signature hold node bin/logbook-verify.mjs record.json
03 · After it finishes · Touchstone
Is the result actually true?
What it gives you. Touchstone takes the claim and tries to break it. Real checks run, what they print is kept, and a plain rule with no AI in it hands down the verdict: supported, refuted, or not provable yet.
An AI proposes the checks as plain lists, a fixed runner executes them and keeps a fingerprint of what they print, a second AI told to refute the claim reads that evidence, and a plain function with no AI in it decides the verdict. The AI plans and reads. It never certifies, and it never touches the shell.
Set out to fail, on purpose.
The judge is told to break the claim, and the planner is asked to include hunts for a counterexample. One surviving refutation beats any amount of supporting evidence. A check that sets out to confirm finds confirmation; this one sets out to fail.
- The order
- Claim, plan, run, judge, gate, dossier
- Verdicts
SUPPORTED(exit 0) ·REFUTED(1) ·UNVERIFIABLE(2)- Fails closed
- No evidence means UNVERIFIABLE. A planned check that could not run caps the verdict, and a blocked check is disclosed, never silent
- Quotes or nothing
- Every line the judge leans on must quote the captured output word for word; the gate strips any that does not, so an invented quote cannot move the verdict
- What you keep
touchstone/dossier@0: the plan, the evidence, the fingerprints and the reasoning, on disk
Run it · from the touchstone repo
# repeatable, zero secrets npm run verify -- verify 'package.json declares "touchstone" as its name' --mock # shell checks wait for an explicit flag npm run verify -- verify "npm test exits 0" --cwd ../some-repo --allow-exec # anchored claims check with no model at all npm run verify -- verify-batch receipts.json --cwd ../some-repo
04 · When someone asks · Binder
Will an auditor accept it?
What it gives you. Binder gives you an answer anyone can check. It turns what your AI already left behind into a pack your auditor can read: each control marked covered, partial or missing, and every line pointing to the record that proves it.
Binder reads run records, claim dossiers, scan exports and trace exports, maps them onto your control framework, and writes down, control by control, what exists and what is missing. A control counts as covered only when a specific file carries a specific supporting claim, cited by path, digest and field.
The disclaimer ships inside the document.
Every pack carries this on its face: this binder is self-assembled evidence, not an attestation, audit opinion, or certification. No underwriter, auditor, or assessor has reviewed it, and nothing here grants, binds, or implies insurance coverage. A pack that overstated itself would be worse than no pack, because the first reviewer to catch it discounts everything else in the file.
- Reads
agent-work/record@0·touchstone/dossier@0· ShipSafe scan exports · OpenTelemetry trace exports- Hands you
binder.mdfor a person andmanifest.jsonfor a machine- Per control
- Covered, partial, missing or not applicable, with the citation that justifies it
- Never writes
- “We probably do this.” An uncited control is reported as evidence absent
- The framework
- Your file, not ours. The bundled one is illustrative and is not the text of any named standard
Run it · from the binder repo
# build it npm install && npm run build # assemble a pack node bin/binder --framework frameworks/your.json \ --evidence ./evidence --out out # covered, partial, missing, with citations cat out/binder.md
The foundation · Primitives
One record shape. One way of signing it.
What it gives you. Every part of Rotairo writes the same kind of record and seals it the same way. So the files work together, any of them can be checked without us, and nothing you keep is locked to our software.
agent-work owns record@0 and is its reference and validator. fleet-attestation is the signing atom, pulled out after a dozen repos rebuilt the same thing on their own: canonical form, Ed25519 tagged with its domain, hash chains, offline checking against a public key. It depends on nothing but node:crypto.
Ride the rails auditors already know.
A sealed record can be expressed as an in-toto attestation in a DSSE envelope, so it travels on supply-chain tooling auditors already meet (in-toto and SLSA verifiers, transparency logs, policy checkers), and nobody has to adopt a private format. The record inside is unchanged; the envelope travels beside it.
- Canonical form
- RFC 8785 (JCS): keys sorted, compact, undefined omitted, non-finite numbers refused
- Domain tag
- A required domain string is mixed into the signed bytes, so one product’s signature can never pass as another’s. That is deliberate
- Clocks
- Timestamps are passed in, never read. The library never touches the system clock: same inputs, same bytes, same signature. The test suite reads the source to hold it to that
- The check
- No dependencies, no network, no floats. It reads the whole file before any cryptography, and fails closed everywhere
- Pinned, not shared
- Each consumer pins a tag and vendors that exact version. There is no floating “latest”
Run it · from the agent-work repo
# write signed intact and tampered examples node cli.mjs demo examples # ✓ INTACT (6 steps, signature: valid) node cli.mjs verify examples/session.intact.json # ✗ TAMPERED, first altered step: #3 node cli.mjs verify examples/session.tampered.json
The format
Read the spec before you trust the check.
Everything needed to write your own checker without asking us anything. The bytes are pinned and the refusal codes are stable.
- The record formatEvery field of a sealed record, and why an unknown field is refused.
- The bytes we signReproduce them exactly and any Ed25519 library will check the signature.
- Checking it offlineFree, no account, no network. Exit 0 when the chain and the signature hold.
- Refusal codesEvery refusal names its cause, and the checker fails closed.
- The sample recordThe file the hero shows. Download it and check it with any tool you trust.