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:

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

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-signature

That 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.