Security & trust

Last updated: October 19, 2026

Verdifax is a security product. This page describes the controls, the threat model, the assumptions, and the limits of what the platform protects against. Security promises are stated narrowly so they can be audited.

Threat model

Verdifax is designed so that a later change to what a run anchors no longer matches its entry on a public log that Verdifax does not operate. Since verifier 0.12.0, an entry counts only if it is signed with a published Verdifax log-signing key and the timestamp the log signs for it verifies, so a rewritten record that anyone else logs as a new entry fails. Verdifax holds that key, so that check alone does not catch a change made by Verdifax itself. From run 240 on, each record seals a 600-second anchoring window: a re-log by Verdifax after that window fails, and moving the record's sealed clock to fit changes the record's own date. Earlier records show their authenticated anchoring time but cannot fail on it. Nothing here shows that Verdifax sealed true inputs in the first place. Two layers are checked separately. Recomputing the execution record and its proof from the disclosed fields needs no key, no public log and no Verdifax service. The public-log checks are a second layer, which relies on the log's key and on Verdifax's published log-signing keys. What a run anchors depends on when it was sealed. On successful runs from run 238 on, it is every section of the record, through a second public-log entry, the complete-record anchor; from run 239 on, deleting that second entry is detectable too. On records sealed before run 238 it is the run's identity and, on leaf v5 records, its 33 pre-anchor values; their descriptive sections are sealed by hashes inside the bundle but are not bound to the public log, so they do not protect against a party able to rewrite the whole bundle. The platform is notdesigned to prevent the user's own program from being incorrect, malicious, or biased, those are evaluation problems upstream of the attestation pipeline.

Cryptographic primitives

Verdifax uses industry-standard, publicly documented cryptographic primitives: SHA-256 collision-resistant hashing, deterministic JSON serialization (compact JSON in a fixed field order for sealed sections and receipts, and for a JSON payload a canonical form with sorted keys), ECDSA P-384 signature verification (for hardware attestation reports), and the Sigstore Rekor public transparency log. We do not invent cryptography; we compose audited primitives. None of this is gated behind an NDA: the verifier is open source, the formal proofs are public at github.com/Verdifax/lean-verdifax, and the verifier's source code shows the exact algorithm behind every check it runs. Zero-knowledge proof generation is on the public roadmap and is disclosed as a scaffold in every audit bundle until it ships.

Hardware attestation

Runs sealed with a SEV-SNP attestation agent carry a genuine AMD SEV-SNP attestation report taken before the run's execution record is built: the report's guest-controlled field is computed from the record's other fields, the report's hash becomes the record's hardware field, and the orchestrator verifies the report's certificate chain against AMD's Milan root of trust (pinned by fingerprint) plus its signature before sealing. A configured agent that fails halts the run rather than sealing it without the hardware. The report and certificate chain are embedded in the audit bundle so any third party can re-verify them offline. The hosted service runs without a hardware agent today; those runs disclose that honestly with a per-run scaffold flag, which reports what each individual run actually carried. Additional platforms (TPM 2.0, Nitro Enclaves) and a multi-vendor quorum root of trust are disclosed roadmap items, not current capabilities.

Determinism guarantees

Identical canonical field sets produce identical hashes byte-for-byte in the Go and Python implementations, locked by shared canonical test vectors. The pipeline's computation is deterministic: replaying the same inputs under a pinned clock and nonce, with the public-log anchoring stage held fixed, reproduces the same manifest hash byte-for-byte, and POST /execute/verify-determinismruns exactly that check. Every run seals its own admission clock and nonce, so two separate submissions of identical inputs are distinct records with distinct manifest hashes, by design. The nonce is the admission time in nanoseconds, a clock reading rather than a random value; the admission clock enters the manifest hash indirectly, through the transport stage's hybrid logical clock, an allowed run's allow token clock and the DKEC stage's epa_hash, which is computed over the admission clock with the envelope identifier, the kernel dispatch order, the program identifier and the registry record hash, all derived from it. A production record's hash also binds its own unique public-log entry, so a production record is verified by recomputing its seals from its recorded fields, not by re-running it. This is a primitive of the system, not an aspiration, it is enforced by the platform's foundational specification.

Independent verification

The open-source verifier, which does not rely on Verdifax's servers or database, recomputes every section hash, the bundle hash, the pre-anchor table digest, the anchored Rekor leaf and, when present, the complete-record anchor from the record's own fields, verifies each Rekor inclusion proof offline, and confirms every section is bound to the same manifest hash. The manifest hash itself is recomputed server-side by GET /runs/{id}/verify, which needs the full stored manifest. The verifier returns either a verified status or a specific failure reason, and it applies the same checks to every bundle, including the Verdifax operator's own. The first public-log entry binds the run's identity and, on records sealed since leaf v5, its 33 pre-anchor values, which include the attested decision, the AIVP governance result, the payload hash, the actor and the scaffold flags. On successful runs sealed from run 238 on a second entry binds every section of the record. On earlier records the descriptive sections (outcome summary, request substance, authorization and approval chain, real-world effects, system provenance) are sealed by hashes inside the bundle but are not bound to the public log, so they show the bundle is internally consistent, not that a party able to rewrite the whole bundle has not changed them.

Use verifier v0.17.0 or later. v0.12.0 also accepts a bundle whose JSON carries a value twice under keys that differ only in letter case, which other readers such as jq, Python and browsers read differently; since v0.12.1 the verifier refuses it. Releases through v0.11.0 accept a record whose public-log entries were logged by any key, and report records from run 240 on as failed because they do not recognize leaf v7. Releases v0.1.4 through v0.9.0 also did not check that an anchor's recorded leaf hash matches the public-log entry its inclusion proof covers; since v0.10.0 the verifier binds every anchor to its log entry. From run 239 on, a record's first public-log entry states that a complete-record anchor is required, and a missing or altered one is a failure (exit code 1). The published log-signing keys, their fingerprints and custody are listed in the verifier's README: key 3, the production key since 15:59:19 UTC on October 24, 2026, is held in AWS Key Management Service, and its private key never leaves it: the server asks KMS to sign each entry. Key 1, the production key before then, was a file on the API server's volume, not in a hardware module; from verifier v0.17.0 on it is accepted only for entries logged up to that moment, so a copy of it cannot vouch for anything logged later. Key 3 signed its first three entries on the day of the switch, before v0.17.0 published it: entry 3441033974, the run entry of private test run 254, and the two entries of private run 255. A server defect, fixed in orchestrator 04d06fd, stopped run 254 before its complete-record entry, so it has no complete record and nothing a verifier can check; its stored run row still reads as successful. Run 238 is the one record with a complete-record anchor but without that requirement: for it, require the verdict VERIFIED AND ANCHORED, COMPLETE RECORD ANCHORED, because deleting its complete-record anchor yields PARTIAL COVERAGE with exit code 3 since verifier 0.16.0 (0 before).

What Verdifax keys sign on the public log

A Verdifax log-signing key signs three classes of entry, and only the first two belong to records. Run entries: the first entry (the leaf) of every successful run, public or private, and, from run 238 on, its complete-record entry (for example, private runs 250 and 251 at 18:03 UTC on October 18, 2026, two entries each). Receipt entries: the receipts of refused and halted runs, from run 241 on. Digest entries: POST /attest, an authenticated endpoint that is turned off on the hosted API from orchestrator 6179d58 (October 19, 2026 UTC) on: it answers 410 to every key and submits and stores nothing, so from then on key 1 signs only Verdifax records. Before that it accepted only keys of Verdifax's own entity (Mesedi's): a new key was unclassified and refused, and from orchestrator 52ba089 (October 19, 2026 UTC) on a key classified as an independent submitter was refused too. It took a 64-character SHA-256 digest the caller computed, with a stated algorithm and subject reference, signed with key 1 a hashedrekord of a leaf that commits to that digest (SHA-256 of a preimage carrying attest:<key id>:<subject>, the digest, a hash of the algorithm and subject, and a nonce; the receipt returned the preimage) and returned a receipt stating only that this exact digest was submitted by this key at this time and has not changed since. Verdifax never saw what is behind the digest, no run was created, and the entry matches no record. Mesedi, Verdifax's sister product (the same legal entity, Verdifax, LLC, doing business as Mesedi), anchored its checkpoints through this endpoint: about one digest per hour from September 4, 2026 until 19:01 UTC on October 19, 2026, when Mesedi was paused, so key 1 signed about 24 such entries a day, with exceptions: none from 17:04 to 23:00 UTC on September 4, while Verdifax's ledger was held in mock mode (orchestrator c7bfd70), and two in each of the three hours named below. Every /attestentry came from Mesedi's API key, the last being entry 3325295191 at 19:01 UTC on October 19, 2026; our own records of each stored call and each run account for every entry key 1 has signed. When a submission got no timely answer, Mesedi resubmitted the same hour's checkpoint, so three hours carry two entries, the first of which matches no stored receipt: 11:02 UTC on October 12 and 12:01 and 14:01 UTC on October 18, 2026. Before the first entry of run 246, the oldest record that is public today (20:02:51 UTC on October 17, 2026), key 1 signed 76 entries that are not hourly digests, from May 13 to October 17, 2026, counted by our own audit of every entry key 1 has signed: 35 are leaves of records we still hold that were withdrawn from public view or kept as test and demo data, 3 are receipts, and 38 belong to runs that were never public (run 244, at 17:15 UTC on October 17, among them). Among them one leaf hash appears 28 times between May 13 and June 3, 2026, and another three times on June 2 and 3: until June 3, 2026 (orchestrator 8514675) identical inputs produced an identical leaf, so the same demo request run repeatedly re-logged one hash; a per-run nonce has been part of every leaf since.

Separately, a run that fails at the public-log stage may have an entry the server never received an answer for; such an entry matches no record and never will. One is known: entry 3319064512, signed by key 1 at 01:23:02 UTC on October 19, 2026, from the attempt before run 253: the server submitted the entry, the log answered after the server's ten-second limit, and the server stored the run as failed; a failed run stores no manifest, so the attempt's leaf cannot be recomputed and the entry cannot be proven to be its leaf. From orchestrator d55a2cc (October 19, 2026 UTC) on, when the log answers late or reports a duplicate, the server looks the entry up by its leaf hash and continues, so a late answer no longer fails the run or leaves an unreferenced entry, and a run that still fails at that stage records the leaf hash it submitted in its error message. The same lookup covered /attest submissions until the endpoint was turned off.

The /attest endpoint is not the SDK's client.attest() call, which submits a run to /execute. An /attest entry was built in the same legacy leaf form as records sealed before run 236, with attest:<key id>:<subject>where a record carries its envelope identifier and the caller's digest where a record carries its execution record hash. Before verifier v0.15.2, a record could therefore be forged from an /attest entry and read as verified and anchored with partial coverage (the pre-v5 tier; it could not reach complete-record coverage, which needs a second entry of a form /attest never produces). Verifiers before v0.15.2 still accept such a forgery, built from any /attest entry whose leaf preimage the forger knows or can rebuild. The preimage was returned to the caller in its receipt and, for Mesedi's entries, was included in the chain exports Mesedi gave its signed-in customers. It can also be rebuilt from a checkpoint's digest alone: the subject follows Mesedi's fixed format (checkpoint/<sequence>/<interval start>), and the nonce is the submission time in nanoseconds, which the entry's logged time bounds, so it can be found by search. From orchestrator 6179d58 (October 19, 2026 UTC) on, no one can create a new /attest entry on the hosted API, but the existing entries stay on the log, so do not rely on a verifier before v0.15.2. Since v0.15.2 the verifier refuses any record whose envelope identifier is not a run envelope identifier (env-followed by the first sixteen hex characters of the payload's admission hash) and any record in a pre-table leaf form whose entry was logged after run 236's first entry (19:51:06 UTC on October 15, 2026), so no /attestentry, past or future, can stand as a record's entry. The envelope identifier is derived from the payload the caller sends, so it repeats when the same payload is sent again (runs 237, 238 and 239 share one) and is not a unique run number; the check works because every /attest preimage carries attest: in that position, which no envelope identifier can. /attest used that prefix from its first version (orchestrator 27da552, 01:47 UTC on September 3, 2026). Use v0.17.0 or later.

Data handling

The API receives the payload, any AI output text, and any metadata a caller sends, in readable form. Verdifax stores a SHA-256 hash of the payload and of the AI output text, not the raw content, and stores caller-supplied metadata exactly as sent, as part of the sealed record. The hosted service sends AI output text to Anthropic's Claude API for its governance review. The API has no field for model weights or identity tokens, but anything a caller places in a metadata field becomes part of the sealed record. Each sealed record also contains hashes of the connecting IP address and user agent: from run 245 on, keyed HMAC-SHA256 hashes under a secret only the server holds (prefix hmac-sha256:), which nobody without that secret can reverse, though Verdifax could by trying every possible address; up to run 244, unsalted SHA-256 hashes, from which anyone can recover an IP address that way. Either kind is the same for every record from one address and user agent, so anyone holding several records can link them. From run 247 on the server refuses a request that supplies its own values for these two hashes. The Privacy policy and the sub-processor list give the full detail.

A sealed bundle is publicly readable, without an API key, only after a Verdifax admin key publishes it, and an admin key can publish only runs it created itself; an admin key can withdraw any public bundle. The 44 bundles published under an earlier rule at the sequential address /runs/{id}/bundle.json were withdrawn on October 18, 2026. Bundles published since are readable only at an unguessable /public/runs/{token}/bundle.json address.

Unsalted hashes from five genuine records sealed before run 245 also remain public as test data in the public verdifax-verify repository and its history: runs 208, 235, 237, 238 and 239 (run 239 came from the same address as run 240). Removing them would mean rewriting that repository's published history, which would break the signed provenance of its existing releases, and copies already made elsewhere would remain. The October 2026 hardware evidence record, sealed before run 245, also carries an unsalted hash, but it is the hash of that machine's own loopback address (127.0.0.1) and reveals no network address.

Production build provenance

The production server is built only by GitHub Actions and carries SLSA Build Level 3 provenance from the SLSA project's container generator, checked with slsa-verifier before every deploy. GET /versionreports the image digest that provenance covers, records sealed from run 249 on state the same digest (both are the server's own statement of what it runs), and the signed provenance for that digest is on the public Sigstore log, where anyone can look it up by the digest and check its signer. That shows a GitHub Actions build of the named commit produced the image; the source itself is private.

Site build commit. verdifax.com and docs.verdifax.com are each built and deployed by GitHub Actions from the commit pushed to their repository. Each page names that commit in a verdifax-build-commit meta tag, and /version.jsonon each site reports the commit and the build time, so a reader can see which build of the site they are reading and whether it has changed. Both repositories are private, so the commit identifies the build but does not let readers inspect its source. Verdifax's own review fails whenever a live site's commit differs from the latest commit pushed to that repository.

Access controls

Multi-factor authentication is enabled on every account that controls production: Fly.io, Cloudflare, GitHub, AWS and Google Workspace. Customer API keys are stored only as SHA-256 hashes. The API separates admin keys from customer keys: admin routes reject customer-grade keys with a 403 response. Deployments are provenance-stamped with the exact source commit they were built from, which every audit bundle records.

Key custody

When Verdifax creates an API key, the server also generates an Ed25519 signing key pair for it and stores both keys, including the private key, in the service database. The actor signature in a sealed record is made by the Verdifax server with that stored key, so it shows that the Verdifax server signed the record with the key assigned to that API key, not that the customer signed it. The key that signs Sigstore Rekor entries is a file on the same server volume. AWS KMS holds the master key for the Cryptographic Record Erasure feature, but that feature's encryption is not yet wired into run sealing, so no stored field is currently encrypted under those keys and an erase request does not remove any stored content.

Vulnerability disclosure

Security researchers may report suspected vulnerabilities to security@verdifax.com. We acknowledge reports within forty-eight (48) hours and aim to triage within five (5) business days. We do not take legal action against researchers who act in good faith and report vulnerabilities to us at security@verdifax.com.

Compliance posture

Verdifax produces sealed records of the events a customer chooses to log, with each run's anchored values bound to a public log as described above. Organizations can use those records as evidence for their own controls. The obligations below rest with the organization, and using Verdifax does not by itself make any organization compliant:

  • EU AI Act, Articles 12, 19 and 26(6): the logging and log-retention obligations rest with the provider and the deployer of the high-risk AI system.
  • NIST AI RMF 1.0: a voluntary framework. Verdifax records can support outcomes described in MEASURE 2.8, MEASURE 3.1, MANAGE 4.1 and MANAGE 4.3.
  • ISO/IEC 42001: certification applies to an organization's AI management system. Verdifax is not certified.
  • HIPAA 45 CFR § 164.312(b): relevant to audit controls, assessed by the customer.
  • SOX § 404: relevant to internal control evidence, assessed by the customer and its auditor.
  • SR 26-2, which replaced SR 11-7 on April 17, 2026: excludes generative and agentic AI models from its scope.
  • GDPR Article 22: Verdifax records can include caller-declared details of human review. Compliance remains the controller's responsibility. Verdifax cannot currently erase caller-declared metadata from sealed records.

Verdifax does not currently hold a SOC 2 report, an ISO/IEC 27001 certificate, an ISO/IEC 42001 certificate, or FedRAMP certification.

What Verdifax does not protect against

To keep the security promise narrow and auditable, we list the out-of-scope items explicitly:

  • Verdifax does not certify that a model's output is correct
  • Verdifax does not certify training-data licensing or representativeness
  • Verdifax does not authenticate the human user, wire your IAM into the inputs you attest
  • Verdifax does not prove that the values inside a payload describe reality (input authenticity is upstream of the pipeline)

The narrow scope is the source of the cryptographic strength.

Contact

Security questions can be sent to security@verdifax.com.