Signing & identity
attest signs attestations with Ed25519 (via Apple’s
swift-crypto). Signing is optional: an unsigned
attestation is still a valid provenance record, so the tool works with zero setup. Signing is what
lets a policy trust a record and bind it to an identity.
Generating a key
attest keygen
# attest · wrote private key to ~/.config/attest/key (0600)
# public key: BASE64_PUBKEY <- copy this into your policy
- The private key (32 raw bytes, base64) is written to
$XDG_CONFIG_HOME/attest/keyor~/.config/attest/keywith0600permissions, created with restrictive permissions from the outset. Keep it secret; it never leaves the machine. - The public key is printed so you can paste it into a policy’s
signerPinning/trustedKeys(see below). It is also embedded on every record you sign, so verifiers need no key server. attest keygenrefuses to overwrite an existing key unless you pass--force.
How a signature is computed
When you pass --sign to attest sign, the signer:
- Computes the canonical bytes of the attestation: sorted-key JSON that deliberately
excludes the
signatureandpublicKeyfields (see Architecture). - Produces a detached Ed25519 signature over those bytes.
- Attaches the base64 signature and the signer’s base64 public key to the record.
attest sign --commit HEAD --reviewer human:leif --confidence 0.95 \
--verdict review --human-approved --sign
Ed25519Verifier.verify reverses this: it recomputes the canonical bytes and checks the embedded
signature against the embedded public key. Because the canonical form excludes the signature pair,
attaching a signature never changes the signed bytes, but any mutation of the content
(confidence, verdict, note, timestamp, reviewer, commit) makes verification fail. An unsigned record
verifies as “not signed”, never as “valid”.
Preventing reviewer spoofing
A reviewer is just a string like agent:claude or human:leif. Nothing stops someone from filing an
attestation that simply claims reviewer: human:leif. allowedReviewers gates the reviewer
string, but it cannot tell a genuine human:leif from an impostor. Two policy rules close that gap
by binding identity to a key.
signerPinning: per-reviewer key binding
signerPinning is a { reviewer: base64 pubkey } map. Any attestation whose reviewer is a key
in the map must be signed with that exact pinned public key and verify. A pinned reviewer that
is unsigned, signed with a different key, or tampered fails the commit. Reviewers absent from the
map are unaffected.
{ "signerPinning": { "human:leif": "BASE64_LEIF_PUBKEY" } }
This is the rule that actually stops human:leif spoofing:
# leif's genuine, signed sign-off PASSES:
attest sign --commit HEAD --reviewer human:leif --confidence 0.95 \
--verdict review --human-approved --sign
attest verify --commit HEAD # exit 0
# a spoof (claiming human:leif unsigned, or signed with another key) FAILS:
attest sign --commit HEAD --reviewer human:leif --confidence 0.95 # unsigned claim
attest verify --commit HEAD # exit 1: reviewer human:leif is pinned but unsigned
trustedKeys: a global allow-list of signers
trustedKeys is a set of trusted base64 Ed25519 public keys. When non-empty, any record that
is signed must verify and carry a publicKey in the set; an untrusted or invalid signed
record fails. Crucially, trustedKeys does not force signing; an unsigned record passes it.
It bounds the universe of acceptable signers.
{
"requireSignature": true,
"trustedKeys": ["BASE64_LEIF_PUBKEY", "BASE64_CI_PUBKEY"]
}
Pair trustedKeys with requireSignature: true to both require a signature and restrict it
to a trusted key.
How the two compose
signerPinningis per-reviewer: only reviewers in the map are constrained, and each must be signed with its exact pinned key. This stopshuman:leifspoofing specifically.trustedKeysis global: it does not force signing, but any record that is signed must use a key from the set.
A policy that pins a human and bounds the signer universe:
{
"requireSignature": true,
"trustedKeys": ["BASE64_LEIF_PUBKEY", "BASE64_CI_PUBKEY"],
"signerPinning": { "human:leif": "BASE64_LEIF_PUBKEY" }
}
The full pinned lifecycle (correct-key PASS, then wrong-key / unsigned FAIL) is demonstrated
end-to-end in
examples/07-signer-pinning.sh.
Scope & limitations
attestuses a single local Ed25519 key per machine and embeds the public key on each record. It is not a CA: key distribution, rotation, web-of-trust, and revocation are roadmap items.- Trust today is expressed by pasting public keys into the policy (
trustedKeys/signerPinning). That is deliberate: the policy file is the source of truth for who counts. - Combine signing with the
maxAgeDaysfreshness rule (see Policy reference) so a trusted signature also has to be recent. A signed sign-off from months ago will not silently keep passing.