Skip to content

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.json on 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:

JSON
{
  "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

Shell
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

Shell
pluck bureau oath fetch openai.com

Output:

oath/fetch: vendor=openai.com bytes=842 envelopeHash=a1b2c3...

To check the envelope offline:

Shell
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?":

Shell
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:

Shell
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):

TypeScript
// 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 contradictAgainstOath keeps 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:

  1. Verify the envelope signature against the signing-key fingerprint embedded in the body.
  2. Confirm vendor matches the Origin where the well-known document was served.
  3. 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: 1 literal.
  • 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. fetchOath refuses 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. contradictAgainstOath keeps evaluating dropped claims until a signed Oath.Retraction/v1 appears.
  • verifyOath is 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

TypeScript
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.
  • Rotatedisclosure-rebuild re-anchors oaths after a key compromise.
  • Fingerprint – silent model-swap detection cross-checks against the oath's scope.
  • Concepts: Act – the underlying contradict verb.
Edit this page on GitHub
Previous
Tripwire

Ready to build?

Install Pluck and follow the Quick Start guide to wire MCP-first data pipelines into your agents and fleets in minutes.

Get started →