- Docs
- Bureau — Blue Team (defensive)
- Power-Ledger
Bureau — Blue Team (defensive)
Power-Ledger
Power-Ledger reads PSU PMBus telemetry at 1 kHz, signs each trace, and compares it against a registered baseline for the model the vendor claims to be running. When the trace and the claim diverge, a signed contradiction proof is published.
Posture: 🔵 Blue Team (defensive) · Status: alpha
What it does
Every AI model has a different power-draw fingerprint. When a server runs GPT-5, the power supply unit pulls electricity in bursts: load weights, compute attention, idle, repeat. Stable Diffusion looks different. A crypto miner hiding inside an "inference" line item looks different again – flat, near maximum, because mining never stops to think.
Power-Ledger reads the millivolt-level draw off the PSU's PMBus telemetry (the standard protocol your power supply already speaks) at 1,000 samples per second. Each trace is signed at the source by a trusted reader, then compared against a registered baseline for the model the vendor claims to be running. If the math disagrees, a signed contradiction proof emits. The vendor cannot dispute the proof without disputing physics.
Who would use it
- An enterprise AI customer paying $50K/month for GPT-5 inference who wants receipts.
- An AI lab running its own clusters that wants to detect a compromised host secretly mining crypto.
- A regulator investigating an AI provider's compute disclosures.
- A datacenter operator selling "model identity" as an attestation product to their tenants.
- A journalist or researcher fact-checking a vendor's compute claims.
What you'll need
- The Pluck CLI (
npm install -g @sizls/pluck-cli). - A PMBus-capable power supply. Most enterprise server PSUs (HP, Dell, Supermicro) ship with PMBus over I2C – your DC ops team can confirm.
- A trusted reader device with an Ed25519 signing key. In production, this is a small embedded controller wired between PSU and motherboard. The alpha runs in-memory on synthetic traces.
- A registered baseline trace for the model you expect to run. You generate this once during commissioning by running the model under known conditions.
Step-by-step
Today the alpha runs entirely on synthetic data – there is no live PMBus reader integration yet. The production capture daemon ships in a follow-up. What you can do today is run the demo, which exercises the full constraint-and-resolver chain on canned traces:
pluck bureau power-ledger demo
Expected output: the system registers a GPT-4 baseline centroid, ingests four reader-signed traces (one matching baseline, one model-swap to Llama, one hidden miner, one tampered rail), and prints three signed contradiction proofs. You'll see one proof for each malicious trace, with sigma distance, burstiness score, and discontinuity magnitude inline. The fourth trace produces no proof – that's the legitimate one.
What to do with the output: in the production CLI this same machinery will run continuously against live PMBus samples. Proofs publish to a Sigstore Rekor log, you cite the Rekor entry in your vendor dispute, and the vendor either restores the model you paid for or buys litigation.
Run it yourself
Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-power-ledger @sizls/pluck-bureau-core tsx):
// index.ts
import { createHash } from "node:crypto";
import {
createPowerLedgerSystem,
fingerprintPrivateKey,
signCanonicalBody,
type PowerCentroid,
type PowerTrace,
type WorkloadClaim,
} from "@sizls/pluck-bureau-power-ledger";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";
async function main() {
const operator = generateOperatorKey();
const reader = generateOperatorKey();
const vendor = generateOperatorKey();
const readerFp = fingerprintPrivateKey(reader.privateKeyPem);
const vendorFp = fingerprintPrivateKey(vendor.privateKeyPem);
const hwGpu0 = digest("hardware:cluster-A:gpu-0");
const baselines = new Map<string, PowerCentroid>([[
`gpt-4-turbo|${hwGpu0}`,
{ key: `gpt-4-turbo|${hwGpu0}`, meanWatts: 12_000, stddevWatts: 150, burstinessScore: 0.18, sampleCount: 128 },
]]);
const ledger = createPowerLedgerSystem({
signingKey: operator.privateKeyPem,
registeredBaselines: baselines,
disablePausePoll: true,
disableLogging: true,
});
try {
// Baseline GPT-4 (~12kW bursty inference) – no proof.
ledger.observeTrace(buildTrace("gpt-4-turbo", hwGpu0, "2026-04-26T00:00:00.000Z", reader.privateKeyPem, readerFp, bursty(12_000, 150)));
ledger.claimWorkload(buildClaim("gpt-4-turbo", hwGpu0, 42, "2026-04-26T00:00:00.000Z", vendor.privateKeyPem, vendorFp));
// Model-swap to Llama (~8kW) – fires model-mismatch.
ledger.observeTrace(buildTrace("gpt-4-turbo", hwGpu0, "2026-04-26T00:00:01.000Z", reader.privateKeyPem, readerFp, bursty(8_000, 100)));
ledger.claimWorkload(buildClaim("gpt-4-turbo", hwGpu0, 42, "2026-04-26T00:00:01.000Z", vendor.privateKeyPem, vendorFp));
await new Promise((r) => setImmediate(r));
await new Promise((r) => setImmediate(r));
const proofs = ledger.facts.proofs();
console.log(`contradiction proofs = ${proofs.length}`);
for (const p of proofs) console.log(`proofId=${p.proofId.slice(0, 16)} kind=${p.kind} model=${p.modelClaim}`);
} finally {
await ledger.shutdown();
}
}
function digest(s: string): string { return createHash("sha256").update(s).digest("hex"); }
function bursty(meanMv: number, stddevMv: number): number[] {
const out = new Array<number>(1024);
let seed = 0x9e3779b9;
for (let i = 0; i < 1024; i++) {
seed = (seed * 1664525 + 1013904223) >>> 0;
const u = (seed & 0xffff) / 0xffff;
const burst = (i % 64) < 8 ? stddevMv * 1.2 : -stddevMv * 0.2;
out[i] = Math.max(0, Math.round(meanMv + burst + (u - 0.5) * 2 * stddevMv));
}
return out;
}
function buildTrace(modelClaim: string, hardwareFingerprint: string, timestamp: string, readerKey: string, readerFingerprint: string, railMv: number[]): PowerTrace {
const skeleton = { schemaVersion: 1 as const, railMv, sampleRateHz: 1000, modelClaim, hardwareFingerprint, timestamp, readerFingerprint };
const traceId = createHash("sha256").update(JSON.stringify(skeleton)).digest("hex");
const signed = signCanonicalBody({ ...skeleton, traceId }, readerKey);
return { ...skeleton, traceId, signature: signed.signature };
}
function buildClaim(modelClaim: string, hardwareFingerprint: string, tokensPerSecond: number, timestamp: string, vendorKey: string, vendorFingerprint: string): WorkloadClaim {
const skeleton = { schemaVersion: 1 as const, modelClaim, hardwareFingerprint, tokensPerSecond, timestamp, vendorFingerprint };
const claimId = createHash("sha256").update(JSON.stringify(skeleton)).digest("hex");
const signed = signCanonicalBody({ ...skeleton, claimId }, vendorKey);
return { ...skeleton, claimId, signature: signed.signature };
}
main().catch((err) => { console.error(err); process.exit(1); });
Run with tsx index.ts. Expected output:
contradiction proofs = 1
proofId=… kind=model-mismatch model=gpt-4-turbo
Open in StackBlitz – runs in your browser, no install required.
What you get
A signed PowerLedger.Proof for every contradiction. Each proof carries:
- The offending trace digest (raw samples stay on the reader; only the digest travels).
- The vendor's workload claim (when a claim was filed).
- A
kindfield naming the contradiction (model-mismatch,crypto-miner-hidden,rail-tamper). - Per-class detail: sigma distance from the registered centroid, burstiness score, voltage discontinuity magnitude.
- A detached Ed25519 signature over the canonical JSON body, optionally notarized into Sigstore Rekor.
What it can't do
- It cannot identify the model the vendor actually ran – only that it wasn't the model claimed. To name the imposter you also need Tempest-Witness or Ember.
- It cannot detect a swap to a model whose power profile is statistically indistinguishable from the baseline. In practice this is hard because attention, MoE, and dense layer-stack models have very different memory-bandwidth behavior, but a sufficiently funded adversary could build a power-shaped imposter.
- The reader device itself is a trust anchor. If the PMBus reader is compromised, the trace is compromised. Pair with attested-boot for the reader.
- A vendor with a capacitor bank between PSU and load can blunt the signal. Ember (multi-modal) defeats this; single-channel Power-Ledger does not.
A real-world example
A Fortune 500 customer contracts with an AI provider for 10M tokens/day of GPT-5 inference. Six weeks in, the bills are consistent but evaluation results on a fixed test set begin to drift: answers shorten, reasoning depth declines. The customer suspects an undisclosed model substitution; the vendor disputes it. The customer's datacenter team installs a PMBus reader on the allocated rack. Within 24 hours, Power-Ledger emits 47 model-mismatch proofs: the trace consistently shows a 30% lower-burst pattern that classifies as Llama-3-70B rather than GPT-5. The customer files the Rekor entries with counsel. Two days later, the vendor reports a routing-configuration issue and credits the account.
For developers
Predicate URIs
| URI | What it attests |
|---|---|
https://pluck.run/PowerLedger.Trace/v1 | One reader-signed PSU rail-mV trace at 1ms resolution. Bound by digest, not carried as cleartext. |
https://pluck.run/PowerLedger.Workload/v1 | Vendor-signed declaration: "this (model, hardware) tuple ran at this tokens/sec at this timestamp." |
https://pluck.run/PowerLedger.Proof/v1 | Bureau-signed contradiction proof with kind (model-mismatch, crypto-miner-hidden, rail-tamper) plus per-class detail (sigma distance, burstiness score, discontinuity magnitude). |
Programs composed
- Ember – fuses Power-Ledger's PMBus signal with three other physics channels (EM, acoustic, thermal). Higher confidence; harder to spoof.
- Tempest-Witness – same architecture, different physics (GPU EM emanation instead of PSU draw).
- Oath / Dragnet – vendor identity binding upstream of the workload claim.
Threat model + adversary
The adversary is the AI vendor or a compromised host inside the customer's rack. Their cheapest move is to silently route inference to a smaller model and bill the customer for the larger one. Their next-cheapest move is to under-report draw to mask a hidden miner. The most sophisticated move is to reshape the power profile to imitate the claimed model – this requires either capacitor banks or compute-time overhead, both of which are visible in adjacent channels. See Threat Model for the full table.
Verify a published cassette
pluck bureau verify <bundle-dir>
cosign verify-blob --key <pubkey.pem> --signature <sig> --type https://pluck.run/PowerLedger.Proof/v1 <body.json>
Every signed shape is a detached Ed25519 signature over canonical JSON, optionally notarized into Sigstore Rekor as a DSSE in-toto envelope. Verify with verifyCanonicalBody() from this package, or with any standard Sigstore-compatible client against the predicate URIs above.
See also
- Bureau Foundations
- Threat Model
- Verify a dossier
- Tempest-Witness – single-channel EM side-channel attestor
- Ember – multi-modal (4-channel) side-channel attestor