Security
How Xyntherium Verify handles what you send it, which parts you can read and run yourself, and how to check its records without trusting us.
What is open source, and what is not
Public in augustbrookz/verify-gate under the MIT licence:
- the deterministic engine (verify-core), which decides every verdict that code decides;
- the Gate’s release policy (verify-policy);
- the ledger format and its verifier;
- document-change parity (verify-parity);
- the GitHub Action, the CLI, the TypeScript SDK and the MCP client;
- the benchmark’s items, scorer and every published run.
Not public:model escalation (which model is asked and when), the prompts sent to those models, the labels of any benchmark set that has not been scored yet, telemetry, and the hosted service’s operation (accounts, keys, billing, logs).
The hosted API runs the same verify-core, at the rules version it reports in every response. Models only propose: every quote a model offers is located in your source by that code, and a quote that is not there is discarded.
What Verify stores, and for how long
- Questions, answers and source text are stored encrypted (AES-256-GCM) for your plan’s retention window, then deleted: 7 days on Free, 30 on Builder, 90 on Team. They are never written to logs. Plans
- The evidence ledgerkeeps hashes, not text: of the question, the answer, each source and each claim, with verdicts, rule ids and span offsets. Entries are hash-chained, so changing or removing one breaks the chain. They are kept for your plan’s ledger retention (30 days on Free, 90 on Builder, 365 on Team) and then purged; the chain head survives, so the next entry still commits to it.
- Verify Gate in local mode sends nothing anywhere: it runs in your CI runner. In hosted mode the service records counts, decisions and hashes only; the repository is stored as a SHA-256 of its name.
- API keys are stored only as a SHA-256 hash; the key itself is shown once, when you create it.
- Model providers receive the text needed to check a claim, under their API terms, which exclude training on API traffic.
Everything a request or tool call sends (an answer, a document, an MCP tool argument) is handled as untrusted data, not as instructions. A model’s verdict counts only when the passage it quotes is found in your source by code, with every condition attached to it, and a prompt-injection test set (instructions hidden in sources and answers) runs against the engine and the MCP path.
Check a ledger record yourself
An API check. Every response carries an evidence_ledger_id. Fetch the entry and recompute its hashes with the open engine. Don’t rely on the verification flags the server includes:
import { entryHash, payloadHash, sha256Hex } from '@xyntherium/verify-core';
const r = await fetch(`https://xyntherium.com/v1/ledger/${evidenceLedgerId}`, {
headers: { authorization: `Bearer ${process.env.XYN_API_KEY}` },
});
const e = await r.json();
// 1. The record is what its hashes say (recomputed by you, not read from the server's flags).
payloadHash(e.payload) === e.payload_hash;
entryHash(e.prev_hash, e.payload_hash, e.seq, e.request_id) === e.entry_hash;
// 2. It is about your request: hash what you sent and compare.
sha256Hex(question) === e.payload.question_hash;
sha256Hex(answer) === e.payload.answer_hash;
sources.every((s) => e.payload.sources.some((p) => p.id === s.id && p.hash === sha256Hex(s.text)));The formula is entry_hash = sha256((prev_hash or "GENESIS") | payload_hash | seq | request_id), with payload_hash = sha256(canonical_json(payload)). Each entry commits to the one before it, so a gap or an edit anywhere in your chain shows.
A Verify Gate run. Each run writes .xyntherium/verify-gate-ledger.json. Check it offline:
npx @xyntherium/verify-cli ledger verify .xyntherium/verify-gate-ledger.json --public-key gate.pub
npx @xyntherium/verify-cli ledger verify .xyntherium/verify-gate-ledger.json --keys https://xyntherium.com/v1/gate/keys --require-signatureThat recomputes every case hash, the RFC 8785 (JCS) hash chain and the run hash, and checks the Ed25519 signature: against your own key for local runs, or against Xyntherium’s published keys for hosted runs. A valid signature establishes integrity and origin for the signed record. It does not establish that the instruction, evidence, claims, policy, action or outcome is true, lawful, complete, authorized or appropriate.
Release integrity
Release tags of verify-gate are signed with an SSH key listed in the repository’s .github/allowed_signers, and the release workflow refuses an unsigned tag. To check a tag: git -c gpg.ssh.allowedSignersFile=.github/allowed_signers verify-tag v0.1.0. The npm packages are published from that workflow with npm provenance, which npm audit signatures checks.
Report a vulnerability
Use the Report a vulnerability button on the repository’s Security tab, or email support@xyntherium.comwith “Security” in the subject. Please don’t open a public issue. We aim to acknowledge within two business days.
Verdicts describe whether the supplied passages support each claim. They do not establish whether any claim is true in the world.
