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 high turns 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

Portcullis, for the person who signs off →

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

Logbook, for the person who signs off →

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

Touchstone, for the person who signs off →

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.md for a person and manifest.json for 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

Binder, for the person who signs off →

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

Primitives, for the person who signs off →