Skip to main content
CanaryVaultsCanaryVaults home
ProductsPricingBlogDocs
Start Free
← Back to BlogPostmortem

We were selling a proof that had never run

August 20265 min readCanaryAudit

Several pages described CanaryAudit records as anchored on-chain. Not one record ever had been, and the code that would have done it was not in the deployed image. Here is what happened, and what we decided instead.


Until this week, several pages on this site described CanaryAudit records as anchored on-chain. The privacy policy listed content hashes published to a public blockchain as permanent and undeletable. The subprocessor page named Base as a service receiving our data. The legal export PDF that ships on Pro printed a summary line reading Anchored on Base.

None of it had ever happened. Not once, for any customer, on any record.

Four things were true at the same time. No row in any table carried a transaction hash. The anchoring script was not in the deployed backend image and no build step put it there — the Docker build context is the backend directory and the script lives at the repository root, outside it. Neither smart contract had ever been deployed; no contract address is committed anywhere in the repository. And the chain we would have been writing to was a testnet that can be reset, which means even a working version could not have honestly been called permanent.

Setting the configuration would not have helped. The process that reads it was never started.

The reason nobody noticed is the part worth reading. The function that looks for the anchoring script returns a result object when it cannot find it, and that branch had no log line at all. The caller read the transaction hash field off that object and discarded everything else, including the reason. So a request to log an audit action returned 201 with a null transaction hash — precisely what a deployment with anchoring deliberately switched off would return — and wrote nothing anywhere. Seven distinct failure conditions, one indistinguishable response, no trace in any log.

We decided not to turn it on. Anchoring to a resettable testnet and calling it durable evidence is a false claim wearing a real transaction hash, which is worse than no claim because it is more convincing. Anchoring to a mainnet means a funded wallet key sitting on the API host — a credential whose theft costs money directly, unlike every other secret in the system — plus a fee on every write and a chain call inside the customer's request. We could not justify that against the thing it would actually buy.

Because the guarantee that matters was already running. Every audit record's content hash is sealed with HMAC-SHA256 under a key held in the application environment and never stored in the database. That distinction is the whole point. An actor with full database write access can rewrite a record and recompute its content hash so the row is perfectly self-consistent — and still cannot produce the matching seal, because the key is not in the database to be found. That is the question a buyer actually asks an audit-log vendor: can you, the vendor, forge my records? The answer is no, and it does not require a blockchain.

One gap remains and we would rather write it down than leave it implied. The verifier treats a missing seal as cannot assess rather than as tampering, because records written before sealing existed legitimately have none. So someone with database write access can delete the seal and quietly downgrade that row to a hash-only check. Closing it needs a recorded sealing epoch, after which a missing seal is a refutation rather than a shrug. That is the next piece of work on this surface, and it is worth more than anchoring ever was.

If you are evaluating a tamper-evidence claim from any vendor, including this one, the question to ask is where the key lives. A hash proves a record is internally consistent with itself. Only a key the database cannot reach proves the operator did not write it.


This article is about a shipped surface: CanaryAudit — tamper-evident action log. Integration details live in the docs.

Continue reading

More notes from the CanaryVaults team.

Featured

What a paste-site canary sees: the anatomy of a credential-stuffing hit

A walkthrough of the seeding pipeline end to end: how a decoy credential ends up on a paste site, what happens in the moments after someone tries to use it, and what lands in your alert channel.

Platform6 min read
Postmortem

Every page on our site was shipping an empty body

One call to useSearchParams() sat inside the root layout's only Suspense boundary, and deopted the entire application to client-side rendering. Twelve words of markup left our server. Nothing in the build said so.

Platform4 min read
Engineering

It worked, it said so, and nothing happened

A contact form returned 201 and showed a green confirmation every time. Nobody was ever notified. We went looking for more of these and found about thirty, all with the same shape.

Platform4 min read
CanaryVaults

Deception-based AI security. Decoys, trap facts, honeypots, prompt defense, and tamper-evident audit trails — one workspace.

Plant your first canary

PRODUCT

ProductsCanaryAgentDashboardPricingReferralGet started

RESOURCES

DocumentationQuickstartShieldEvidence formatAPIBlog

COMPANY

AboutSecurityReport a vulnerabilityContact

TRUST

Trust centerVerify evidenceStatusChangelogIncidentsDPA

COMPARE

vs Thinkst Canaryvs CanaryTokensFor SaaS teams

LEGAL

TermsPrivacyCookiesSubprocessorsSupport
deception-based AI security© CanaryVaults · canaryvaults.comsha-256 sealed · tamper-evident

CANARYVAULTS