Skip to content
BitExactdocs
Documentation
On this page

Reporting a security issue#

Send reports to security@bitexact.ai. Please include the affected component (recorder, viewer, SDK, CLI, or the open verifier), the version (bitexact version, or bitexact-verifier --version), and the steps that reproduce it. Reports about the verifier are especially welcome: it is the trust root that auditors and counterparties rely on, and a verifier that accepts evidence it should refuse is the most serious class of issue this product can have.

Please do not open a public issue for a vulnerability, and do not test against a deployment you do not operate. BitExact is self-hosted: there is no BitExact-run service holding customer evidence, so there is no hosted system in scope.

What a fix looks like, when one ships:

  • The product: a new release with a SHA256SUMS file and a cosign-signed image, delivered to every licensed customer, with the issue described in its release notes.
  • The verifier: a new version with a row in CHECKSUMS.txt and a verifier-v<version> tag, so anyone holding a bundle can re-verify it with the fixed verifier.

The machine-readable contact for each of our sites is at /.well-known/security.txt.

The product's own integrity#

The product ships compiled: a release carries none of its source. The open verifier is the exception by design - its own package, never compiled, so the file an auditor reads is the file that runs.

ThreatWhat detects it
A shipped file is modifiedEvery release is signed, and the product checks at startup that it runs the files its release shipped. A build that fails the check records nothing - a declared build_modified gap - passes the agents' calls, and says so in its health.
A license is forged or self-signedLicenses are accepted only when the vendor's hybrid key signed them, both halves.
A license is copied to another deploymentIt binds to the deployment's recorder key, and the deployment it came from is evident in the evidence the copy records.
A lapse or tamper is hiddenOne standing rule feeds health, the readiness probe and every run's run_meta; the open verifier prints each run's stated standing and fails a run that recorded while its standing said it must not.

The honest limit: these controls detect root-level tampering on the host; they do not prevent it. That is why the verifier prints a run's standing as the producer's statement, never as proof. Every such act also breaches the license agreement, which prohibits reverse engineering and modification and gives BitExact an audit right.

Sensitive values in the evidence#

The capture policy (Capture controls (customer docs)) decides what the evidence keeps of each field before anything is written.

What it guarantees. It runs where every entry is built and where every remote event enters a host's collector, so a value it scrubs or keeps hash-only never reaches a spool, the wire, a store, the index, a log line or a backup. Card numbers (issuer prefix and Luhn), IBANs (mod-97) and credentials are scrubbed everywhere by default; US Social Security numbers, email addresses and your own patterns are detectors a policy turns on. Every outcome other than kept is declared inside the entry hash and every signature over it, and a verdict counts the declarations, never tokens in the text. A field it cannot scrub in time is withheld and declared a gap, never written raw; the agent's own call is never touched. The proxy's stream journal, which holds a response before the policy has run on it, is always sealed under the host key.

Turning it off is declared. With BITEXACT_CAPTURE_POLICY=off every value is kept as captured: the run records off, every verdict over it says no capture policy was in force, and health raises capture-policy-off (critical when BITEXACT_ENV=prod). A host whose policy cannot be loaded records no run rather than one without the controls it was asked for: the agent runs on, the unrecorded run is noted under capture_policy_invalid, and health raises capture-policy-invalid. A collector running with no policy forwards raw values; the recorder applies its own before anything is stored and raises collector-unscrubbed, but those values crossed the wire and sat in the recorder's spool.

Key custody. A hash-only field is committed under the capture key, which the evidence never holds. You keep it apart from the evidence, as the recorder key is kept, and can rotate it or declare it revoked from the date it was lost.

What it is built against. The values it protects are the raw sensitive values an agent sends and receives; the capture key; and the declarations that say what was removed. The parties it is built against are an outsider with a copy of the store, a backup or an evidence pack; an insider with store access but no keys; a root user on one agent host; and a developer whose agent emits sensitive data by accident. The threats it answers include guessing a value from the evidence, values dressed to slip past a detector, an insider claiming a scrub removed a value, a policy switched off, input crafted to stall the detectors, over-scrubbing, and a stolen capture key; each is stopped, or declared in the evidence.

What it does not do. It finds only what its detectors find: a card number split across fields, written in words or inside base64 (declared unscanned; the pci preset keeps it hash-only) is not found, and names, addresses and other free text are not detected at all - clean them before BitExact sees them and say so with scrubbed-upstream, which is recorded as your claim, not checked. A hash-only field is pseudonymous, not de-identified: it is not HIPAA de-identification, and under GDPR the evidence is still personal data. A linkable hash-only field shows anyone which fields hold the same value. The policy does not change what the agent sends or receives, and it does not reach evidence recorded before it was in force.