Security
The standards the receipts are built on, the boundary that keeps verification independent, and the honest limits of what a chain proves.
Standards
RFC 8785
JSON Canonicalization Scheme. Every receipt is canonicalized before it is hashed or signed, so the same receipt always produces the same bytes. The verifier implements it separately from the code that writes receipts.
SHA-256
Each receipt carries the SHA-256 hash of the complete receipt before it. Changing any receipt changes its hash, every link after it, and the chain head.
Ed25519
Each receipt is signed with the recording agent’s own key, created locally. The verifier checks every signature against the public keys carried in the bundle.
RFC 9421
HTTP Message Signatures authenticate an agent to the collector with the same key that signs its receipts: method, authority, path and creation time are always covered, a body is covered by its digest, and replays are rejected by a 60-second window and a nonce cache. Receipt submission over HTTP is not built yet; only this authentication layer is.
RFC 3161
Trusted timestamps from an authority the operator does not control will anchor the chain head when you buy an evidence bundle, once bundles open (nothing can be bought yet), and, once Cloud is available, continuously. The verifier checks the full token offline and reports whether the authority is in your trust list separately from whether the token is sound, because a bundle can embed a self-signed root.
The receipt shape aligns with draft-sharif-agent-audit-trail, an individual draft with no IETF standing, and departs from it where documented: notably Ed25519 where the draft specifies ECDSA P-256. We claim alignment with that draft, nothing more.
The open-core boundary
The code is split into three packages. Core (MIT) records, chains and verifies receipts locally. The verifier (MIT) checks evidence bundles offline. The Cloud package (commercial) holds the anchoring and bundle export. Core never imports Cloud. The verifier imports neither Core nor Cloud, and no network module, so checking the paid tier’s output never requires the paid tier, and it runs on an air-gapped machine. These rules are a source scan that runs as the first CI step, not a convention.
What a self-hosted chain does and does not prove
A self-hosted chain is self-attested. It proves internal consistency: every receipt is intact, correctly signed and linked to the one before it. It does not prove the operator left history alone. An operator with database access and the agents’ signing keys can rebuild the whole chain, and local verification will pass. A test in the codebase does exactly that, then confirms the anchored bundle still fails, because the one thing the operator cannot forge is the timestamp authority’s signature. The local append-only database triggers stop application bugs and accidents, not a determined operator.
| Property | Local chain | Anchored chain |
|---|---|---|
| Detects casual tampering | yes | yes |
| Detects a full rebuild of anchored history by the operator | no | yes |
| Detects a rebuild made before that history was anchored | no | no |
| Detects backdating after the anchor | no | yes |
| Detects truncation of the un-anchored tail | no | no |
The last row is why Cloud (early access, not available yet) is planned to anchor continuously: new receipts would be batched and the chain head anchored at most every 15 minutes, which bounds the window in which a truncation goes undetected. Receipts also describe only what an agent chose to record; they make recorded behavior tamper-evident, not unrecorded behavior visible.
Keys and identity
A receipt’s signature says which key signed it, not who holds that key. This is how agent keys are handled today.
Where the signing key lives
lacre initcreates an Ed25519 key pair and writes it toagent.key.jsonin the store directory:./.lacreif that directory exists, otherwise~/.lacre, unless--storeorLACRE_HOMEnames another. The file is written with mode 0600 and holds the secret key unencrypted. There is no HSM or KMS support yet, so protect that file as you would any credential.It never leaves the machine
Receipts are signed locally. The secret key is never sent or logged: what leaves the machine is signatures and public keys. A bundle is made from your chain file, which holds receipts and public keys, and we never ask for the key.
Registration: trust on first use
initregisters this machine’s agent against its public key. By default the agent id is derived from the key (urn:lacre:agent:followed by its RFC 7638 thumbprint), so a different key gives a different id.agent registercan bind a chosen id, or enrol a remote agent from a public key you were given out of band. The first key bound to an id stays bound to it: registering the same id with a different key is refused.Rotation and revocation
Neither exists yet.
keygen --forcearchives the old key file and writes a new key, which is a new identity: receipts already written still verify under the old key, but the old id cannot be rebound. There is no way to revoke a key. Anyone who holds the key file can sign new receipts as that agent and, with database access, rebuild history that is not yet anchored; a rewrite of history already anchored still fails verification.Who vouches for the public keys in a bundle
Only the bundle. The verifier checks each signature against the public key the bundle carries for that agent, so a bundle someone built with keys of their own verifies too. Pin your agents’ keys out of band: note each key id when you enrol an agent (
lacre agent listshows it, and every receipt names its key inlacre.key_id) and compare them with the bundle, the same way you pin the timestamp authority’s fingerprint.
Report a vulnerability
Report security issues privately through GitHub private vulnerability reporting: report a vulnerability on GitHub. The report stays private between you and the maintainers until an advisory is published. Please do not open a public issue for a vulnerability.
In scope: the code in the source repository, which is Core, the verifier, the Cloud package and this site. Anything that lets an altered or forged receipt, chain or bundle verify matters most. The same contact is published in /.well-known/security.txt.