@axiorank/log-witness
v0.1.0
Published
A self-hostable witness for an AxioRank audit transparency log. Countersigns each sealed signed tree head with an Ed25519 key you control, so your records are an independent split-view defense. Zero runtime dependencies.
Maintainers
Readme
@axiorank/log-witness
A self-hostable witness for an AxioRank audit transparency log.
AxioRank's audit log is tamper-evident on its own (Merkle-sealed, signed tree heads). What a log operator cannot prove by itself is that it never showed two different histories to two different observers, the "split view." The defense is external witnessing: an independent party countersigns each sealed head. If the log ever forked, the witness's signatures over the two heads expose it.
This package is that witness. You run it on infrastructure you control, with an Ed25519 key only you hold. Point a workspace's witness URL at it, and every sealed head is countersigned by a key AxioRank does not have, so your records are a genuinely independent attestation.
Run it
npx @axiorank/log-witness --port 8787Generate a stable key (do this once, keep it secret):
node -e "const {generateKeyPairSync}=require('node:crypto');console.log(generateKeyPairSync('ed25519').privateKey.export({format:'der',type:'pkcs8'}).toString('base64'))"AXR_WITNESS_NOTE_KEY="<base64-from-above>" \
AXR_WITNESS_NAME="note:acme-witness" \
npx @axiorank/log-witness --port 8787Without AXR_WITNESS_NOTE_KEY an ephemeral key is generated and a restart
invalidates every record. That is fine for a quick try and never for production.
Optionally require a shared bearer on the witnessing endpoint:
AXR_WITNESS_AUTH_TOKEN="..." npx @axiorank/log-witnessHTTP API
| Route | Purpose |
| --- | --- |
| POST /witness | Body { signedTreeHead, signedAt }. Returns a WitnessRecord. |
| GET /key | { witness, keyId, publicJwk }. Pin this out-of-band. |
| GET /healthz | Liveness. |
A WitnessRecord is an Ed25519 signature over
canonicalize({ subjectHash, witness, signedAt }), where subjectHash is the
SHA-256 of the canonical signed tree head with its own signature stripped. It is
verified by @axiorank/audit-verify
and the @axiorank/log-watchdog
monitor.
Connect it to AxioRank
In Settings → Security → Witnesses (Enterprise plan), add this daemon's URL. AxioRank POSTs each sealed head to it and stores the returned record alongside the checkpoint, exposed on the public log so anyone, including you, can verify the split-view defense with the watchdog.
Programmatic use
import { createWitnessServer } from "@axiorank/log-witness";
const witness = createWitnessServer({ name: "note:acme", rawKey: process.env.KEY });
await witness.listen(8787);Zero runtime dependencies; built on node:http and node:crypto. MIT licensed.
