Countersigning public key
Everything you need to check an App Garden sign-off record yourself, without the add-in and without trusting us at the moment you check it.
The key
ECDSA, curve P-256, SHA-256. SPKI, base64url, no padding.
Key id: appgarden-csl-2026-09-11
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8G66G01uRbZKtO4HftENIssV3goga3XAU38YzkW6phY-9mp6aMeABibArGE0N7ftoE-cQbCkGtch7SroxXdPpA
The private half exists in exactly one place: the licence Worker, as a secret. It is not in the add-in, not in this repository, and not on any machine we develop on.
What the signature is over
The signature is over the record's recordHash, as its 64 ASCII hex
characters — not over the decoded bytes, and not over the record body directly.
recordHash is the SHA-256 of the record body, which is a newline-separated
list of name=value pairs in a fixed order:
v
canon
ruleset
logic
inputs
results
signer
role
revision
comment
officeUser
timestamp
inputRanges
outputRanges
findings
prev
Values are escaped: a backslash becomes \\, a newline
\n, a carriage return \r. That escaping is not cosmetic — without
it a comment containing a newline could forge an extra field, and a signer could make the
signed body read as though the revision were something else.
The field order is fixed in code rather than taken from the object, because a record that has been written into a workbook, read back and re-parsed must produce the same bytes it did when it was signed. Otherwise the signature would fail on a file nobody had touched.
Where the record lives
Inside the workbook, in a custom XML part under the namespace
https://appgarden.co.uk/calc-sign-off/record/1. A second part under
.../index/1 holds the per-cell index used to list changes on a red
verification; it is deliberately not covered by the signature, because tampering
with it can only make the change list wrong, never make a changed workbook verify green.
Key rotation
Every record names the key that signed it in its keyId field. If the key is
ever rotated, records signed under the old one stay verifiable — the add-in and this page
carry both.