Audit chain
Every administrative act hash-chained; seal it under a key held off the box, and anchor it with a signature your auditor can check alone.
This one sits outside the eleven-step request path: it governs what happens around a governed call rather than inside one.
the whole pathOn this page
An append-only table enforced by the person you are constraining is not a control
The value of an audit log in an audit is that it answers who widened this policy, when, and whether anyone edited the record afterwards. That means the threat model has to include the operator: an administrator who can relax a policy, run an agent against it, then delete the row that says so. Database permissions do not help when the whole database is a file on that operator’s disk, and immutability asserted by configuration is not evidence.
The alternatives were considered and rejected for stated reasons. An external write-once store makes tampering hard at the infrastructure layer and is a genuine complementary control, but it proves nothing cryptographically, cannot be verified offline, and adds a cloud dependency to a product that gets deployed on-premises and sometimes air-gapped. A Merkle tree with signed tree heads and a trusted timestamp authority is strictly better — inclusion proofs would let you hand a regulator a verifiable subset without exposing the whole log — and it costs a network dependency on a path that has to stay local, for a capability nobody has yet asked for. The per-record hash chain was chosen knowing what it does not do.
What it does not do is stated in the product’s own documentation rather than discovered during your evaluation. The chain alone is only evidence against an attacker who cannot recompute it; whoever can write the database can rewrite an entry and re-hash everything downstream, and a test in the repository asserts exactly that — the forged chain passes verification on a default install, and the same test asserts that it fails once a key is configured, so neither half of the claim can drift.
Everything above the bare chain exists to move the target. Keying the digests moves it from whoever can write the database to whoever holds the key. Signing a head with Ed25519 and publishing it off the box moves it again, to whoever holds the signing key and can also reach every copy you took. None of those steps reaches tamper-proof, and Token Observe does not use the word.
How it actually works
- 01
Append
Each entry stores the previous entry’s hash and its own digest over that hash plus the canonical JSON of its content, with the row’s own hash and its prevHash both excluded from that JSON so a later recomputation covers exactly what the original covered. Genesis is 64 zeros. The storage adapter reads the chain tip and appends in one transaction, so concurrent writers cannot fork the chain. - 02
Seal
When an audit MAC key is configured, every boot writes a checkpoint — a MAC over the sequence number and the chain hash it reached, under its own domain prefix. Entries written before the key existed cannot be re-MAC’d, because doing that is the act being prevented, so the checkpoint over the head they add up to is what vouches for them. - 03
Verify
A walk checks every link, recomputes every content digest, and checks the sequence for gaps including a missing prefix. It reports the first sequence at which the chain breaks, the number of entries checked, whether the protection was keyed or unkeyed, and which checkpoint the run verified against. - 04
Anchor
With an anchor signing key configured — without one, anchoring is off and the status endpoint says so rather than staying quiet — Token Observe signs a canonical statement of the head on a schedule and on demand for an admin: install id, anchor sequence, previous anchor hash, head sequence and hash, entries covered, protection level, the checkpoint sequence when the chain is keyed, creation time, key id, algorithm and cadence. It refuses to sign at all when the chain does not verify. - 05
Publish
Each anchor is delivered to a local JSONL file sink, an HTTP sink, or both — whichever the deployment configured — in signing order, from a durable per-destination cursor that advances only after that sink acknowledges that exact anchor. Delivery never blocks a boot or a governed request; the anchor is already durable locally before anything is sent, and a sink that cannot be constructed is reported at error level rather than dropped. - 06
Check
An auditor verifies the anchor file with a public key that arrived out of band — never from the file — using a standalone script that needs no install, no database and no network, or by calling the verification endpoint, which holds the database and walks the entry chain first.
What the hash covers, the three failures it locates, and the one it cannot see
The digest input is the previous entry’s hash, a separator, and the canonical JSON of the entry with both its own hash field and its prevHash removed — the prevHash is already the prefix, so it is covered once rather than twice. Canonical means object keys sorted recursively and no whitespace, so one record has exactly one encoding and the digest is reproducible by anyone holding the row. Removing the row’s own hash is not tidiness: a recomputation that included it would cover a field the original write never covered, and every verification would fail.
A verification walk finds three distinct classes of failure and names which one it found. A link mismatch means an entry no longer carries its predecessor’s hash. A content mismatch means the entry was altered. A sequence gap means a row was removed — including a missing prefix, because a database writer who deletes entry 1 and recomputes an unkeyed remainder from genesis would otherwise leave a chain that begins at 2 and checks out link by link. Sequence numbers come from an auto-increment high-water mark that an ordinary delete does not lower, so entries removed from the middle — or from the end, once anything is logged afterwards — leave a hole that no amount of re-hashing can close.
What no walk can see is truncation where nothing is logged after it, or truncation by someone willing to edit that high-water row too. Lopping entries off the end leaves every remaining link honest, so a shortened chain verifies clean, and nothing inside the database can fix that — the length would have to be attested somewhere the same operator cannot edit. That is what the head endpoint is for: it returns the attested head and the entry count with a note telling you to record them outside the deployment before a restore drill, and the verification endpoint takes them back as an expected sequence and hash. The result then says what it was checked against — an operator-supplied head, a stored one, or none — and the deployment guide used to make a bare passing result the success criterion of the restore drill, which is how a restore that had lost 10 of 90 entries passed it.
A caller-supplied expectation is treated as diagnostic evidence rather than trusted runtime state. A mistyped hash must not be able to latch the whole control plane unavailable, so an expectation mismatch is reported and does not trip the corruption latch that an intrinsic failure does.
- Link
- Each entry carries the previous entry’s hash, so an inserted, reordered or replaced row breaks the sequence of links at a known point.
- Content
- The digest is recomputed from the stored row, so an edited actor, action, resource or detail field no longer matches the hash written for it.
- Sequence
- Gaps are located, including a missing prefix, so a deletion cannot be closed by re-numbering the survivors.
- Head
- An expectation is only an expectation if it was written down somewhere else first; a head read from the restored file cannot detect a restore that lost its tail.
hash = digest( prevHash + "|" + canonicalJson(entry without prevHash and hash) )
genesis prevHash = 000…000 (64 zeros)
digest = SHA-256, or HMAC-SHA256 when an audit MAC key is set
GET /api/audit/verify?expectedSeq=90&expectedHash=… after a restore
{
"valid": false,
"entriesChecked": 80,
"brokenAtSeq": 90,
"reason": "chain is truncated: it no longer reaches sequence 90,
which was previously attested",
"protection": "keyed",
"attestedAgainst": "operator"
}Keyed audit epochs, and why this digest is not the one everything else uses
Set an audit MAC key and entry digests become HMAC-SHA256 under it, with the chain head sealed at every boot by a checkpoint MAC. Rewriting an entry now requires the key as well as database access: without it an attacker can only recompute with plain SHA-256, which no longer matches, and with it they would not need to rewrite anything by hand. The guarantee holds only if the key is injected from a secret manager the database administrator cannot read — a key in a file beside the database buys nothing.
The keyed digest is deliberately a separate operation from the plain SHA-256 the rest of the product uses, and the reason is worth stating because it is the sort of thing that gets discovered the expensive way. Agent credentials are looked up by the SHA-256 of the token, so keying that digest would make every token already issued across your estate unresolvable overnight. The audit digest is an integrity tag that nothing looks anything up by, so it can be keyed safely; the identifier cannot.
Entries written before the key was configured cannot be re-MAC’d — that is precisely the act being prevented — so they are covered instead by the checkpoint over the head they reached, and altering any of them moves that head. Unkeyed hashes are tolerated only as an unbroken prefix inside the checkpointed range: once an entry has verified under the key, an unkeyed successor is the forgery, not a legacy row. First enablement is an external two-boot ceremony, and from then on an ordinary keyed boot refuses a missing checkpoint schedule even when the surviving rows form a perfectly valid plain-SHA chain — which is what stops a database writer deleting every checkpoint, recomputing the chain unkeyed, and asking the product to seal the downgrade as if it were first enablement.
Rotation is authenticated rather than inferred. Checkpoint MACs identify their key epoch cryptographically, so old rows need no migration and no key-id column. The configured ring runs newest to oldest, a checkpoint activates its key for entries after the head it seals, and moving back to an older position is refused: trying every key forever would let possession of any retired key authorise new history, which is the thing rotation exists to stop. Two checkpoints at one sequence under different keys are refused as well, because same-sequence transitions have no authenticated order — record at least one audited action under the old key before the maintenance boot. The ring is bounded at 64 predecessors, which is a finite ledger lifetime rather than key expiry: before that boundary you retain a final off-box anchor, archive the old database with its complete ring read-only, and start a separately identified chain.
One small piece of this deserves naming on its own. A missing key and an altered entry produce the same bytes — the hashes do not match — and completely different events: one is a configuration mistake an operator made five minutes ago, the other is an accusation of tampering. When a verification fails at or after a sequence a checkpoint seals, and no key is configured in this process, the result says the key is suspected missing and the console says so in those words, explicitly labelling it a diagnosis rather than proof. The console previously reported the second for the first, which means the product accused the customer’s own operators of the exact act it exists to detect, over a dropped environment variable.
unkeyed keyed
edit a row, do not re-hash detected detected
delete a row from the middle detected detected
delete the tail, keep logging sequence gap sequence gap
delete the tail, stop logging off-box head off-box head
rewrite and recompute every hash UNDETECTED detected
rewrite an entry written before the key UNDETECTED detected
delete the checkpoints n/a boot refuses
rewrite, then restart to re-seal UNDETECTED needs the one-shot flag
read the key out of the host UNDETECTED UNDETECTEDAnchors: evidence a regulator can check without being handed a forging key
A MAC has a structural problem as evidence: the key that verifies the chain is the key that can forge it. Hand it to an auditor and you have handed them the power to fabricate the very record they were given it to check, so they cannot verify independently and you cannot prove you did not. Ed25519 splits those two powers — the install signs, everybody else verifies — and it ships in Node’s own crypto module, so it adds no runtime dependency to a product that gets deployed air-gapped.
An anchor is a short statement, canonicalised and then signed: install id, anchor sequence, previous anchor hash, the chain head being attested, entries covered, the protection level the chain itself had at signing time, the newest checkpoint sequence when the chain is keyed, the creation time, the key id, the algorithm and the cadence. Every field is inside the signature; there is no envelope the signer did not commit to. The stored columns a dashboard or a SQL query displays are checked against the signed statement, and a disagreement makes the anchor untrusted rather than being quietly rendered — if they differ, the displayed values are unsigned. The creation time being inside the signed bytes is a deliberate contrast with the checkpoint, whose timestamp sits beside the MAC and is therefore editable without invalidating anything. The link hash between anchors is deliberately plain SHA-256 rather than the keyed audit digest, because an outside verifier holds the public key and nothing else: the signature is what makes each link unforgeable, the hash only has to be reproducible.
Anchors are chained to one another and numbered contiguously, which is what turns a deleted anchor into a visible break rather than a re-numberable hole. Two validly-signed anchors sharing a sequence with different heads are a self-contradiction the install signed itself into — both bear its key, so it cannot disown either — and that is checked first and reported as equivocation, because reporting it as a broken link would send an auditor looking for a deletion that did not happen. Later anchors may not attest a shorter chain or fewer entries than earlier ones. Anchoring also refuses to sign at all when the chain does not verify, when the head has moved backwards, or when an already-anchored entry no longer matches the hash that was attested. That is the opposite of a retry, and it is the feature: signing over a forged head would launder the rewrite under a key the auditor was told to trust, while not signing leaves the previous anchor standing — and that anchor still contradicts the rewrite. Every refusal is logged at error level and returned by the manual endpoint as a 200 carrying the status and the reason, because an operator pressing the button on a broken chain needs to be told exactly why it refused, not handed a 500.
Changing the anchor signing key produces one dual-signed transition. The first anchor under the new key embeds a canonical transition statement, under its own independent signing domain, that both the retiring and the incoming key sign; the incoming key then signs the containing anchor. It binds the unchanged install id, the sequence and previous-anchor link, the attested head, entries covered, protection and checkpoint state, the asserted time, the cadence and both key ids, so the hand-off is independently checkable without rewriting an old anchor or trusting a key carried beside the evidence. Verification treats the current key plus the supplied previous keys as one ordered current-to-oldest ring: a complete ledger must begin under the oldest key, move one position at each dual-signed transition, never return to a retired key, and finish under the current one. The retiring private key exists only for that one-shot transition, and the product refuses to start if it is left configured afterwards.
Publication is restart-safe and at-least-once. A durable per-destination cursor, keyed by a one-way digest of the destination rather than its raw path or URL, advances only after that sink has acknowledged that exact anchor, so a crash between acknowledgement and cursor commit replays one anchor and never skips one — a missing transition is replayed before any later anchor. The verifier accepts an exact duplicate and rejects a same-sequence duplicate whose displayed envelope was altered. HTTP receivers get the install id and anchor sequence as an idempotency key, and the contract is easy to get wrong in one specific way: a receiver must answer 2xx to an anchor it already holds, because a 4xx is treated as permanent, so a receiver that returns 409 on a duplicate wedges the pipeline at that sequence and every later anchor silently stops publishing while the local ledger keeps growing and looks healthy.
- Key theft
- Anyone who can read the private key out of the process or the KMS can sign any history they like. Asymmetry moves the target from whoever can write the database to whoever holds the key; it does not remove it.
- Pre-anchor history
- Anchor 1 attests the head as it stood when anchor 1 was made. It says nothing about whether the entries beneath it were honest already.
- Sink collusion
- Suppression is only detectable if a copy exists somewhere the install cannot reach. If every sink is administered by the party that runs the database, deleting the anchors and the rows is one action, not two.
- Time
- The creation time cannot be edited after signing, but it is asserted by the signer at the moment of signing. Only a trusted timestamp authority or a public ledger proves when, and neither is built.
The verifier a regulator runs on a locked-down laptop
The standalone verifier takes a JSONL anchor export and an Ed25519 public key, checks every signature and every link, and prints trusted or not trusted. It needs no Token Observe install, no SQLite, no database file, no network and no configuration. Its only imports are the product’s core package — the same pure domain module the server itself verifies with, so there are not two implementations of the rule to disagree with each other — and Node’s own built-ins. Nothing native is loaded, which is what makes it runnable on a machine an auditor is not allowed to install anything on.
It can do that because the domain module is platform-free by construction: hashing arrives through a hasher port and signing through a signer port, so the script supplies both over Node’s crypto module in a few lines and the entire rule set comes along unchanged. Its signer’s sign function throws on purpose — this tool holds no private key and must never be mistaken for something that can produce an anchor, because a verifier that can also sign is exactly the symmetric-MAC problem anchors exist to escape.
The public key is a required argument and is never defaulted — not from the anchor file, not from the install’s own status endpoint, not from the signer’s own key. That is the single decision separating anchoring from theatre: if a key could be defaulted from the material being verified, an attacker would ship their own key beside their own rewritten anchors and every check would report success. Without one the tool exits with a usage error and refuses; it will never print a pass. The same rule governs the verification endpoint, which requires the caller to name the key and returns an untrusted verdict with a reason rather than falling back to the key it is holding.
One gap is printed rather than hidden. Passing the live head catches a tail lopped off below the newest anchor, but at a daily cadence the normal state of a live chain is that it has grown past its newest anchor, and then that flag cannot speak at all — an entry beneath the anchor could have been rewritten and two ordinary entries appended over it. The result reports this as a live-chain check that was not performed, in the human-readable output as well as the JSON; it was previously in the JSON only, so an auditor running the documented command could not tell a fully checked pass from a partly checked one. Closing it needs the chain’s hash at the anchored sequence, which only a caller holding the database can supply and which the script has no flag for today — so for that half of the question an auditor has to use the endpoint.
So the endpoint and the script are not substitutes. The endpoint holds the database, walks the entry chain first and refuses to evaluate the anchors at all if that walk fails; the script runs against a copy taken months ago. An auditor who can reach the running install should use both — the endpoint to confirm today’s history is intact, and the offline script to confirm that today’s history is the same one they were shown then. Neither replaces the other, because the endpoint is served by the party under audit.
- A pass says
- Every anchor in the file was signed by the holder of one of the keys you supplied, each key change was dual-signed by adjacent keys, the anchors are contiguous and correctly linked, and attested heads only move forward.
- A pass does not say
- That the history beneath the first anchor was honest, that the signer’s clock was truthful, or that someone who stole the signing key could not have produced the same file.
- Partial exports are explicit
- A file that does not start at anchor 1 is rejected unless you pass the partial flag, because otherwise a deletion of the first three anchors looks identical to never having had them.
# once, at key generation. The private half goes to a KMS; the public half
# is the only thing an auditor ever needs.
openssl genpkey -algorithm ed25519 -out anchor.pem
openssl pkey -in anchor.pem -pubout -out anchor.pub
# months later, from anywhere: no install, no database, no network
node scripts/verify-anchor.mjs --anchors anchors.jsonl --public-key @anchor.pub
# after one rotation, previous keys newest to oldest
node scripts/verify-anchor.mjs --anchors anchors.jsonl \
--public-key @anchor-new.pub \
--previous-public-key @anchor-old.pub
# exit 0 trusted · 1 not trusted · 2 usage errorExports are digest-sealed and carry the verdict; they are not signed
A compliance export is one bundle containing the traces and their events for the period, the approvals with approver identity, timestamp and rationale, the audit entries covering the governance-plane changes in that window and within the requester’s evidence scope, the chain verification result, and a SHA-256 digest over the canonical JSON of the bundle body, generated at a recorded time. Be precise about what that digest is: it is a seal, not a signature. It lets a recipient confirm the file is byte-for-byte the one whose digest they were given through some other channel, and anyone who can rewrite the bundle can recompute it. Durable origin evidence comes from the keyed chain plus an anchor you retained off the box, not from the export.
What makes the bundle more than an exported log file is the verdict travelling inside it. The chain verification block carries whether the chain is valid, how many entries were checked, the first broken sequence number if there is one, the protection level and the checkpoint the run verified against. A bare valid flag invites the reader to assume more than an unkeyed chain offers — it means nothing was altered without recomputing, not that nothing was altered — so naming the protection level is the difference between an auditor being informed and being misled.
The bundle is capped: at most 200 traces, 5,000 events, 1,000 approvals and 2,000 audit entries, over a default 30-day window. Traces, events and audit entries each set a truncation flag inside the file; the approvals cap is the one that does not, so for approvals the count in the bundle is what you have to read against the period you asked for. The console repeats the trace cap beside the download button and tells you to check the truncation flags before you send the file on, on the reasoning that nobody opens a sealed JSON file to check before forwarding it to a regulator.
Verification remains O(n) over the chain, which is a real cost and is managed rather than hidden. Request-triggered verification reads at most 512 primary-key-ordered rows per chunk and yields to the event loop between them, so gateway, readiness and kill-switch work still progresses and audit-row memory stays bounded independently of history size. One process-wide in-flight walk, a ten-second snapshot cache keyed on the storage engine’s cross-connection commit counter, and a ten-second completion-based cooldown stop a privileged caller turning the evidence check into overlapping event-loop work; throttled callers get a retry-after header. A commit by another connection during a walk invalidates the verdict rather than letting a mixed snapshot be cached or signed, and boot performs the full streamed walk separately, before the server listens. If that walk finds intrinsic corruption, the detecting request receives the finding — or the one evidence bundle carrying it — and Token Observe does not append an audit row onto the chain it has just proved unsafe; the verdict then latches, so readiness, later audit and domain writes and later governed requests fail with a 503 until the process is restarted, while authenticated audit diagnostics stay readable.
Erasure and retention do not break any of this. Traces and the audit log are separate tables and the chain covers governance-plane changes rather than payloads, so a retention pass or a data-subject erasure leaves verification passing, and the record that the deletion happened survives the deletion — with the erasure entry carrying a digest of the subject identifier rather than the identifier itself, so the erasure record does not become a new copy of the thing that was erased.
- Sealed
- SHA-256 over the canonical JSON of the bundle body, with the algorithm and what it covers stated in the file itself.
- Not signed
- There is no Ed25519 signature over an export. The signature lives on the anchors, which is where an off-box copy makes it worth something.
- Labelled
- Protection level and the verified checkpoint travel with the verdict, so a reader is never left to infer which guarantee applies to them.
What this does not do
Stated here rather than discovered during an evaluation. Every line below closes off a reasonable assumption a reader would otherwise carry into a proof of concept.
- No Merkle tree and no inclusion proofs. An anchor covers a head, not a per-entry proof, so you cannot hand a regulator a verifiable subset of the log without handing over the rest of it. The architecture decision defers this deliberately rather than having overlooked it.
- No trusted timestamp. An anchor’s creation time is inside the signed bytes and cannot be edited afterwards, but it is asserted by the signer at the moment of signing. Only a timestamp authority or a public ledger proves when, and neither is built.
- No defence against host compromise. Anyone who reads the audit MAC key out of the process environment, or the anchor private key out of the KMS, can produce a history that verifies perfectly. No in-database scheme survives that, and Token Observe does not pretend to.
- No certifications. Token Observe has no SOC 2 report, no ISO 27001 certificate and no independent penetration test. It is a control that helps you evidence clauses in the EU AI Act, ISO/IEC 42001 and the NIST AI Risk Management Framework — not a certification, and not a substitute for your own deployer obligations.
- No after-the-fact redaction of an audit entry. Editing one to remove something breaks verification from that sequence onward, which is the design working as intended, so the governance-plane log is the wrong place to put anything you may later be required to erase.
If one of those limits is the thing that decides it for you, say so and you will get a straight answer about whether it is on the roadmap or out of scope.
Talk it throughWhat this leans on
Flight recorder
Every governed request in a timeline a compliance officer can read, and a search box that never writes SQL.
Human approvals
One human decision, bound to one exact payload, spendable once.
Policy engine
One deterministic verdict on every governed request: allow, block, redact, or park it for a human.
Is the audit log tamper-proof?
No, and Token Observe will not use the word. Unkeyed, which is the default, the chain is tamper-evident: any edit or deletion that does not also recompute every downstream hash breaks verification at a named sequence number. An operator with write access to the database file can recompute it, and the repository’s own forgery test asserts that the forged chain passes. Configure an audit MAC key from a secret manager the database administrator cannot read and a rewrite needs the key as well. Retain an Ed25519 anchor off the box and any rewrite made after you took that copy is contradicted by it. That is as far as it goes.
What does an auditor need in order to verify an anchor, and what does a pass mean?
Two things, and the second must arrive out of band. First the anchor export — the JSONL sink file, or the anchors endpoint. Second the current Ed25519 public key and, after any rotation, every retired public key in newest-to-oldest order, obtained from a key ceremony, a published fingerprint or an earlier compliance export, never from the anchor file. A pass says the holders of those keys signed every anchor, that each key change was dual-signed by adjacent keys, that the anchors are contiguous and correctly linked, and that attested heads only move forward. It does not say the history beneath the first anchor was honest, that the signing clock was truthful, or that nobody stole the key.
Are compliance exports signed?
No. A bundle is sealed with a SHA-256 digest over the canonical JSON of its body, generated at a recorded time, and it carries the chain verification verdict: valid or not, entries checked, the first broken sequence if any, the protection level and the checkpoint it was verified against. The digest lets a recipient confirm the file has not changed since someone told them what the digest was; it is not a signature, and whoever can rewrite the bundle can recompute it. Durable origin evidence comes from the keyed chain plus an anchor you kept off the box, which is where the Ed25519 signature actually lives.
What happens when verification fails?
It depends on what failed, and the distinction is deliberate. Intrinsic corruption — a content hash that does not match, a sequence gap — is returned to the detecting request as a finding rather than an error, logged at error level immediately, and latches the install: readiness, later audit and domain writes and later governed requests fail with a 503 until restart, while audit diagnostics stay readable. Token Observe will not append an audit row onto a chain it has just proved unsafe. A caller-supplied expected head that does not match, or a concurrent commit during the walk, is diagnostic or transient and latches nothing, so a typo cannot take your control plane down.
Can the keys be rotated?
Both of them, through explicit ceremonies rather than by inference from database rows. Rotating the audit MAC key needs the retired keys as an ordered newest-to-oldest ring plus a one-shot flag for exactly one read-only maintenance boot, which verifies every old epoch and seals a checkpoint under the new key; remove the flag and restart before admitting traffic. Checkpoint MACs identify their epoch cryptographically, so no row is rewritten and no key-id column is added. Rotating the anchor signing key produces one dual-signed transition anchor. The ring is bounded at 64 predecessors, and losing a historical public key makes that epoch permanently unverifiable, so it travels with every backup.
Does erasing a customer’s data break the chain?
No. Traces and the audit log are separate tables and the chain covers governance-plane changes rather than payloads, so a retention pass or a data-subject erasure leaves verification passing — and the record that the deletion happened survives the deletion, with the erasure entry carrying a digest of the subject identifier rather than the identifier itself. The reverse is true and worth planning for: an audit entry cannot be edited to remove something without breaking verification from that sequence onward, so the governance-plane log is not the place to put anything you may later be required to erase.
Prefer to ask a person? Write to us →
Bring us the agent you are least comfortable with.
Write to hello@tenhaw.com with what your agents do, which providers they call and what would have to be true for you to put something in front of them. James Rooney replies. You will get a straight answer about whether Token Observe fits, including when it does not.
no form · no qualification step · no sales desk · the other three ways in