Creating a governed brain
Authority starts at the genesis and nowhere else. Keys are ordinary SSH keys — the private half stays in yourssh-agent, a hardware token behind one, or a KMS that presents as one; the SDK never reads a
private key and defines no format for one.
subject names who holds the key, as an actor identifier.
It is the one thing that connects a signature to the actor a provenance record names — without it, provenance
says a person and the signature says a fingerprint, and nothing asserts they are the same.
It is not a certificate. A subject is asserted by this brain’s governance and nowhere else: changing
one changes the trust root, which is a revision, which needs a quorum. It is as trustworthy as that quorum
and no more. Whether a key really belongs to a particular person in the world is still settled outside the
protocol — what a subject adds is that once it is settled, the conclusion lives where a verifier reads it
instead of in a maintainer’s memory.
AgentSigner.suggested_subject offers the key’s ssh-agent comment when it already is an actor identifier,
which it usually is. Offered, never adopted: the comment is a label the key’s own holder typed, so a quorum
has to make the claim deliberately.Signing and verifying
Which actors a signature stands behind
A provenance record names who performed an operation. Until a key stands behind that name it is a declared identifier — whoever can write to a brain can write any name into its audit trail. Once the trust root names subjects, the two can be compared:asserted and legacy are kept apart because the remedy differs: an unvouched actor needs a governance
act, and a legacy identifier needs a rewrite nobody can perform on bytes already published.
Three rules do most of the work:
- Scopes are computed, never believed. What a snapshot required comes from its difference against its
first parent — canonical gained blocks →
ingest, canonical lost blocks →drop:canonical, a derived module changed →commit, the trust root changed →govern, a module’s signedtombstoneslist grew →redact. Thescopesa signature claims aid diagnosis and decide nothing. - The trust root in force is the parent’s. A revision’s own signatures answer to the list it is replacing — which is why a key cannot admit itself (see below).
- Missing evidence widens the requirement. A truncated chain or an unreadable composition makes the
verdict
insufficient_evidence, never a smaller demand: a verifier that quietly computed less from a truncated history would be exploitable by shipping a truncated history.
A stranger’s key: attributable, or an impersonation
The same snapshot, signed by the same key the trust root does not list, means two opposite things depending on the claim made for it — so the report says which question it answered:- Offered — someone hands you a history for review. The signature verifies, the author is
cryptographically identified, and no authority attaches: the state is
attributable, and the blocks are judged one at a time through validation, where a proposer’s identity earns them nothing anyway. This is how an open project hears from people it has never admitted. - As a head — a registry, a mirror, or the contributor’s own tag serves it as the brain’s current state. The same signature is now an unauthorized key, reported as one, and refused.
HEAD is the default because it is the safe answer: a caller who does not say is asking about a
brain’s current state. plan_reconcile sets OFFERED for you and reports the result on the plan:
Admitting a key: the quorum rule
Changing the trust root is a trust-root revision: a snapshot that changes the key list and nothing else, covered by at leastgovern_quorum signatures from distinct keys holding govern in the revision
before the change. A single owner admits a second key in one call:
created_at, so two constructions would sign different bytes — and the exact bytes
travel by any channel; nothing in them is secret, and each party inspects what it signs:
countersign refuses mechanically what a reviewer would refuse by reading: a parent it cannot see,
content smuggled into a governance act, a trust root that does not advance, an admission claim the
observable chain refutes. A failed quorum advances nothing.
Keep a margin. A quorum equal to the number of govern holders is legal and permanent: lose one
key and neither the remaining holders nor the attacker can assemble the signatures to record a
compromise or admit a replacement, while a stolen key keeps signing within its scopes. There is no
recovery path inside the protocol — re-anchoring would be exactly the self-assertion the quorum rule
exists to forbid — so init and rotate warn when you enter that state, and the report names it:
govern signatures the
previous list demands. This fails with no pin at all, and the rejection propagates: every descendant
of the forged revision stands on authority that was never granted.
Retirement and revocation, without clocks
The two look similar and behave oppositely. Both are trust-root revisions under the same quorum:- Retired (
retired_from, a revision number): everything the key signed while authorized stays valid. An ordinary departure is harmless, and a verifier reportsretired_keydistinctly — collapsing it into “unauthorized” would invalidate history for an administrative reason. - Compromised (
compromised_from, a snapshot digest): every signature at and after that position is withdrawn, even though it was signed while the key was listed. The only construct in the protocol that invalidates a previously valid signature, reported in the report’s separatewithdrawnlist.
The pin: the one thing from outside
Trust cannot be manufactured from inside a system. The pin reduces the exposure to a single decision:pull, the manifest’s trust-root annotation is compared before any module layer is
transferred; when it differs, only the small documents move until the custody walk decides.
A pin is for one brain, and a brain is its genesis. Tags are re-assignable and the trust root
rotates, so neither identifies a brain; the genesis digest never changes. pin() records it, and the
verifier checks it first — otherwise an anchor taken for one brain would be evaluated against
another’s chain, and match or mismatch would both be answers to the wrong question.
Publication: signatures accumulate around the artifact
A signature is never a layer of the brain manifest — countersigning would change the brain’s digest, and a brain must not change identity because someone agreed with it. Each record is the single layer of its own manifest, whosesubject names the brain: in a registry, that is an OCI referrer; in the local
layout, one more index.json entry, which is exactly what an export carries. push publishes them,
pull and fetch collect them, and a transport that never learned about referrers still moves the brain
— its consumers simply see it unsigned, and the push says so.
Install-time tolerances are a VerificationPolicy, not a hierarchy of flags:
The evidence carries its authorship
Every query result reports both verifications, separately:Without the extra
The Ed25519 mathematics lives in the optional extra —pip install 'pyboltzmann[authenticity]'. It buys
exactly one operation. Everything structural works on a plain install: parsing, fingerprints, the trust
root, quorum arithmetic, and the one rejection the paper requires of every reader — a record whose named
fingerprint disagrees with the key inside its own signature blob. What a bare install cannot do is reach
authorized: an unchecked signature reports unverifiable, because “could not check” is a different
fact from “failed”, and a missing dependency must never read as a forgery — or as a pass.
Signing through an
AgentSigner works without the extra: the mathematics runs inside the agent,
which is the point of never holding the key.Verification security floor
Boltzmann signatures pin SSHSIG’s message hash to SHA-512. A generic SSHSIG document using SHA-256 is valid SSH syntax, but it is not a valid Boltzmann signature. Verification also requires canonical Ed25519 point encodings,S below the group order, the strict cofactorless equation, and a public key
outside the small-order subgroup. ssh-dss is always refused; RSA keys below 3072 bits are refused
before the SDK reports whether that algorithm is implemented.
Golden vectors
Two published files pin this behaviour for any implementation, in any language:sshsig.json carries the wire format — including the signed-data blob, which is what tells a framing bug
from a signing bug — and signatures.json carries whole chains, published test key pairs, and the
verdict a verifier MUST reach for each of the paper’s worked cases. AuthenticityConformance in the
conformance suite replays them against any store.