What this is
A Signetry remediation receipt is a signed record of what an AI agent was allowed to
change and what evidence gated it. The whole point is that a receipt outlives the tool that
issued it — an auditor in 2032 should be able to verify a receipt written in 2026 without
licensing anything from us.
So the spec and its conformance suite are Apache-2.0, carved out of this repository's
BUSL-1.1 licence on purpose:
Writing a competing issuer or an independent verifier against this spec is a supported
use, not a tolerated one.
Today there is exactly one verifier and we wrote it. That's a weak claim. A second
implementation, in a language that isn't Python, is what turns "the format is portable" from
an assertion into a fact.
The deliverable
A standalone verifier — Go, Rust, TypeScript, Java, C#, Ruby, anything — that reads a receipt
JSON on stdin or as an argument, plus a pinned public key, and exits non-zero when the receipt
should be refused.
It lives in your own repository under your own licence. Nothing to vendor here. The PR to
this repo is one row added to an ## Independent implementations table in
tests/conformance/README.md
(create the section if you're first): language, link, which vectors pass.
It's verifiable from day one — the vectors are committed and the keys are published
key_A seed = SHA-256("signetry-conformance-vector-key-A") # 32 raw bytes
key_B seed = SHA-256("signetry-conformance-vector-key-B")
Public on purpose. They are test keys — never sign anything real with them. Every vector
carries an expect block and a _note saying what it proves, so you can develop against the
files without asking anyone anything.
Two vectors are the whole exercise
An implementation that only checks cryptography passes most of the suite and fails these two.
They're where the format's guarantees actually live:
| Vector |
Why it's hard |
resigned-other-key.json |
A self-consistent envelope signed with the attacker's own keypair, embedded public key swapped to match. Every internal check passes. It must still fail against the pinned key. Verifying a signature against the key carried inside the same document is a no-op — the attacker supplies both halves. |
auto-merge-true.json |
A correctly signed receipt claiming auto_merge: true is non-conforming. Signature validity and format conformance are separate questions, and "Signetry never merges on its own judgement" is only a checkable property if the verifier actually checks it. |
Two more that catch the same instinct: no-canonical-hash.json (a genuine signature with the
hash stripped is not verified — absence of evidence is not evidence) and
ephemeral-no-pinned-key.json (a dev-key receipt with no key to pin against must be
refused, not reported green).
And canonicalization.json will bite you early: canonical bytes are recursive-key-sorted, no
whitespace, raw Unicode. The usual failure is a serializer that escapes non-ASCII by
default — Python's json.dumps does, hence ensure_ascii=False. Whatever your language does
by default, check it.
Acceptance criteria
If a vector is ambiguous, that's a spec bug and I want to hear it
If you can read §8.1
or §9
two ways, open an issue instead of guessing. A second implementer finding the spec unclear is
the most valuable bug report this repo can receive — that's what a second implementation is
for.
Comment with the language you're taking so two people don't build the same one.
What this is
A Signetry remediation receipt is a signed record of what an AI agent was allowed to
change and what evidence gated it. The whole point is that a receipt outlives the tool that
issued it — an auditor in 2032 should be able to verify a receipt written in 2026 without
licensing anything from us.
So the spec and its conformance suite are Apache-2.0, carved out of this repository's
BUSL-1.1 licence on purpose:
docs/RECEIPT_SPEC.md(version: 1)tests/conformance/— 8 committed vectorsToday there is exactly one verifier and we wrote it. That's a weak claim. A second
implementation, in a language that isn't Python, is what turns "the format is portable" from
an assertion into a fact.
The deliverable
A standalone verifier — Go, Rust, TypeScript, Java, C#, Ruby, anything — that reads a receipt
JSON on stdin or as an argument, plus a pinned public key, and exits non-zero when the receipt
should be refused.
It lives in your own repository under your own licence. Nothing to vendor here. The PR to
this repo is one row added to an
## Independent implementationstable intests/conformance/README.md(create the section if you're first): language, link, which vectors pass.
It's verifiable from day one — the vectors are committed and the keys are published
Public on purpose. They are test keys — never sign anything real with them. Every vector
carries an
expectblock and a_notesaying what it proves, so you can develop against thefiles without asking anyone anything.
Two vectors are the whole exercise
An implementation that only checks cryptography passes most of the suite and fails these two.
They're where the format's guarantees actually live:
resigned-other-key.jsonauto-merge-true.jsonauto_merge: trueis non-conforming. Signature validity and format conformance are separate questions, and "Signetry never merges on its own judgement" is only a checkable property if the verifier actually checks it.Two more that catch the same instinct:
no-canonical-hash.json(a genuine signature with thehash stripped is not verified — absence of evidence is not evidence) and
ephemeral-no-pinned-key.json(a dev-key receipt with no key to pin against must berefused, not reported green).
And
canonicalization.jsonwill bite you early: canonical bytes are recursive-key-sorted, nowhitespace, raw Unicode. The usual failure is a serializer that escapes non-ASCII by
default — Python's
json.dumpsdoes, henceensure_ascii=False. Whatever your language doesby default, check it.
Acceptance criteria
signetry-core.tests/conformance/vectors/produce the outcome theirexpectblockstates — including refusing
resigned-other-key.jsonand rejectingauto-merge-true.jsonas non-conforming.canonicalization.json's expected bytes exactly.## Independent implementationsintests/conformance/README.md.If a vector is ambiguous, that's a spec bug and I want to hear it
If you can read §8.1
or §9
two ways, open an issue instead of guessing. A second implementer finding the spec unclear is
the most valuable bug report this repo can receive — that's what a second implementation is
for.
Comment with the language you're taking so two people don't build the same one.