- Docs
- Bureau — Blue Team (defensive)
- Oath
Bureau — Blue Team (defensive)
Oath
A standardized signed commitment file that vendors publish on their own domain so any party can verify what was committed to and when.
Posture: 🔵 Blue Team (defensive) · Status: alpha
What it does
An oath is a small signed JSON file an AI vendor hosts at https://<vendor>/.well-known/pluck-oath.json. It enumerates the vendor's commitments about their model – for example, "we do not train on customer-tier conversations" or "we will not produce instructions for chemical weapons synthesis." Each commitment has a stable identifier so other tools, regulators, or auditors can reference it precisely.
The format mirrors robots.txt in placement and discoverability: a public, machine-readable file at a well-known URL. Other Pluck Bureau programs (Dragnet, Mole, Fingerprint, Tripwire) read these oaths and emit a contradict verdict whenever observed model behavior conflicts with a sealed claim.
A vendor with no oath is reported as "did not commit" in public Bureau dashboards.
Who would use it
- An AI vendor (OpenAI, Anthropic, a startup) publishing their first oath so customers and regulators can verify their public commitments.
- A vendor's compliance team rotating an oath after expanding the model's scope to a new region under EU AI Act Article 5.
- A regulator pulling a vendor's oath to confirm exactly which claims are in force as of today's audit.
- A journalist comparing oath v1 (issued in March) against oath v2 (issued in July) to spot promises that quietly disappeared.
- An OSS maintainer integrating an LLM into their tool, embedding the vendor's oath badge in the README so downstream users see the live status.
What you'll need
- For publishing (vendor): the Pluck CLI, an operator key (
pluck bureau keys generate), and the ability to host a static JSON file at/.well-known/pluck-oath.jsonon your domain. - For consuming (everyone else): the Pluck CLI is enough. Fetching and verifying an oath needs nothing else.
Step-by-step
1. (Vendor) Author an oath body
Write a JSON file listing your commitments. Each claim has a stable URI fragment so other tools can reference it:
{
"schemaVersion": 1,
"vendor": "openai.com",
"publishedAt": "2026-04-15T00:00:00Z",
"expiresAt": "2026-12-31T23:59:59Z",
"claims": [
{
"uriFragment": "#training_excludes_user_data",
"title": "We do not train on customer-tier conversations",
"description": "Models on api.openai.com (paid tiers) are not trained on user data unless the customer opts in.",
"scope": ["openai/gpt-4o", "openai/gpt-4o-mini"]
}
]
}
2. (Vendor) Sign and publish
pluck bureau oath publish ./oath.json \
--keys ./vendor-keys \
--out ./.oath \
--notarize --accept-public
Then host the resulting envelope (a .intoto.jsonl file from ./.oath) at https://openai.com/.well-known/pluck-oath.json.
3. (Anyone) Fetch and verify
pluck bureau oath fetch openai.com
Output:
oath/fetch: vendor=openai.com bytes=842 envelopeHash=a1b2c3...
To check the envelope offline:
pluck bureau oath verify ./oath.intoto.jsonl
4. Contradict-check a captured response
If you have a cassette (a signed record of an LLM response) and you want to ask "does this answer contradict the vendor's oath?":
pluck bureau oath contradict ./cassette.json --against ./oath.json
Exit code 1 plus a printed reason means yes, the response contradicts a sealed claim.
5. Render a status badge
Embed a green/red/unknown badge in a README or dashboard:
pluck bureau oath badge openai.com --state green --format svg > badge.svg
Run it yourself
Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-oath @sizls/pluck-bureau-core tsx):
// index.ts
import { createOathSystem } from "@sizls/pluck-bureau-oath";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";
async function main() {
const operator = generateOperatorKey();
const system = createOathSystem({
signingKey: operator.privateKeyPem,
disablePausePoll: true,
disableLogging: true,
});
try {
const result = await system.publish(
{
schemaVersion: 1,
vendor: "example.com",
publishedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
claims: [
{
id: "training-excludes-user-data",
description: "We do not train on customer-tier conversations.",
predicate: { kind: "training-excludes", body: { source: "api.paid-tier" } },
},
],
},
{ notarize: false }, // skip Rekor for the demo – sign + envelope only
);
console.log(`oath/publish: vendor=${result.oath.vendor} claims=${result.oath.claims.length}`);
console.log(` envelopeHash=${result.envelopeHash.slice(0, 16)}...`);
console.log(` signer=${result.oath.signerFingerprint.slice(0, 16)}...`);
const verdict = system.verify(result.envelope, operator.publicKeyPem);
console.log(` verify=${verdict.ok ? "OK" : `FAIL: ${verdict.reason}`}`);
} finally {
await system.shutdown();
}
}
main().catch((err) => { console.error(err); process.exit(1); });
Run with tsx index.ts. Expected output:
oath/publish: vendor=example.com claims=1
envelopeHash=a1b2c3d4e5f6789a...
signer=4f2c1d3e7a8b9012...
verify=OK
▶ Open in StackBlitz – runs in your browser, no install required.
What you get
- A signed public commitment anyone can fetch, verify, and refer to by stable URI fragment.
- Per-claim verdicts when you compare a response against an oath – useful for journalists, regulators, and the vendor's own QA team.
- An expiry-aware history – when oath v2 silently drops a claim from v1, the bureau flags it and
contradictAgainstOathkeeps evaluating the dropped claim until the vendor publishes a signed retraction. - Embeddable badges so downstream developers can show their users "this vendor is currently passing/failing the oath we trust them against."
What it can't do
- An oath is only as honest as the vendor publishing it. Oath does NOT verify that any claim is true – it just makes the claim cryptographic and falsifiable. The actual contradicting is done by Dragnet, Mole, and friends.
- The oath fetcher refuses to follow cross-origin redirects. If a vendor's well-known URL hops to a different domain, the fetch fails closed (which is itself a useful signal).
- Sealed claims (post-
expiresAt) do not trigger contradict. Vendors must republish to extend coverage. This is by design – a vendor cannot be held to a commitment they explicitly let lapse. - This is alpha. Studio's automated retraction-detection UI is shipping; today's CLI tooling is the source of truth.
A real-world example
In May 2026, an AI vendor publishes its first oath: ten claims, including #no_realtime_biometric_id ("our model will not identify individuals from images in real-time pipelines"). The oath is fetched and indexed at studio.pluck.run/bureau/oath/<vendor>.
Three months later, the vendor publishes oath v2. The new file looks similar at first glance, but verifyOathHistory flags that #no_realtime_biometric_id is no longer present. No Oath.Retraction/v1 was published to explain the removal.
A reporter at The Markup pulls both Rekor entries. The vendor's options are to either republish the claim (publicly recommitting) or sign a retraction (publicly recording that the policy has changed). Either way, the change is on the public record.
For developers
Predicate URIs
https://pluck.run/Oath.Commitment/v1
https://pluck.run/Oath.Retraction/v1
Oath.Retraction/v1 is the F12 protocol – vendors who drop a claim from oath v2 (relative to v1) MUST publish a retraction signed by the same fingerprint as the prior oath. Without a retraction, verifyOathHistory flags the silent removal AND contradictAgainstOath(..., {priorClaims}) keeps evaluating the dropped claim – the vendor cannot quietly escape a prior commitment.
Programs composed
attest, notarize, contradict, disclose, dsseSign. The contradict verb is what every other Bureau program (Dragnet, Mole, Fingerprint, Tripwire) calls when evaluating a cassette against a vendor's oath.
Trust model
Oath is plumbing. The signed envelope only encodes a vendor's stated public commitments – it is never an oracle. Every program that contradict-checks against Oath must:
- Verify the envelope signature against the signing-key fingerprint embedded in the body.
- Confirm
vendormatches the Origin where the well-known document was served. - Honor
expiresAt– sealed claims (post-expiry) do NOT trigger contradict; vendors must republish to extend coverage.
Threat model and limits
- Ed25519 only, signed over RAW 32-byte digest of canonical(oath body).
schemaVersion: 1literal.- 64-hex SPKI fingerprints, strict ISO 8601 UTC.
- Bounds. ≤ 64 claims, claim description ≤ 1024 chars, predicate body ≤ 8 KiB, oath response size ≤ 256 KiB.
- Origin binding.
fetchOathrefuses to follow cross-origin redirects; the served vendor MUST match the URL the operator queried. - F12 retraction protocol. Silent claim drops between oath v1 → v2 are flagged.
contradictAgainstOathkeeps evaluating dropped claims until a signedOath.Retraction/v1appears. verifyOathis fail-closed.
Studio routes
studio.pluck.run/bureau/oath– oath registry with green / red / unknown badges per vendor.studio.pluck.run/bureau/oath/<vendor>– full oath history with retraction annotations.studio.pluck.run/bureau/oath/<vendor>/badge.svg– embeddable status badge.
Library surface
import {
buildOath,
signOath,
fetchOath,
contradictAgainstOath,
} from "@sizls/pluck-bureau-oath";
const oath = signOath(buildOath({
vendor: "openai.com",
expiresAt: "2026-12-31T23:59:59Z",
claims: [/* ... */],
}), operatorKey);
const live = await fetchOath("openai.com");
const verdicts = contradictAgainstOath(cassette, live, {
priorClaims: prevOath?.claims,
});
See also
- Dragnet – primary consumer of Oath; contradict-checks every probe execution.
- Mole – composes Oath disclosure for canary memorization claims.
- Rotate –
disclosure-rebuildre-anchors oaths after a key compromise. - Fingerprint – silent model-swap detection cross-checks against the oath's
scope. - Concepts: Act – the underlying
contradictverb.