- Docs
- Bureau — Blue Team (defensive)
- Embodied-Ledger
Bureau — Blue Team (defensive)
Embodied-Ledger
After an autonomous-platform incident, post-event analysis typically depends on logs the manufacturer controls. Embodied-Ledger signs the (sensor-frame, world-model, motor-command) triple at the source so the resulting record is tamper-evident and independently verifiable.
Posture: 🔵 Blue Team (defensive) · Status: alpha
What it does
Modern autonomous machines (delivery drones, robotaxis, warehouse arms, surgical robots) all share one causal chain: a sensor frame arrives, a world model interprets it, a motor command is emitted. Embodied-Ledger attests that chain. For every perception → decision → action cycle, the agent emits a signed triple: (sensor-frame digest, world-model output digest, motor-command digest, timestamp, agentId). Tamper with any one of those three links and the chain breaks in a way a verifier can detect.
Four tamper classes are checked. Command-without-frame – a motor command was emitted but no signed sensor frame exists in the preceding window (the robot was acting blind, either because perception failed and the command should have been gated, or because the chain was spoofed at command-emit). World-model-skip – a command claims to derive from a world-model digest that doesn't appear anywhere in the agent's signed chain (forged or retroactively swapped). Out-of-envelope – a motor command's reported magnitude exceeds the OEM-signed safety envelope (asking 50G of a robot rated for 5G). Frame-replay – the same sensor-frame digest appears at two distinct timestamps (the textbook way to spoof a perception subsystem). Raw sensor bytes (camera, lidar, audio) NEVER leave the device. Only the digests do.
Who would use it
- A robotaxi operator (Waymo, Cruise, Zoox) needing tamper-evident incident records – the manufacturer's editable logs are no longer the only story in court.
- A regulator (NHTSA, NTSB, FAA) reviewing post-incident records of an autonomous system that hurt someone.
- A plaintiff's attorney suing after a robot-induced injury who needs records the defendant manufacturer can't quietly amend.
- A surgical-robotics manufacturer producing FDA-mandated tamper-evident logs for adverse-event reporting.
- A logistics-warehouse operator (Amazon FC, Ocado) running fleets of pick-arms and AMRs (autonomous mobile robots) that need defensible incident records.
- An autonomy-stack maintainer (ROS, Apollo, Autoware) wanting first-party signed-triple support shipping with the upstream stack.
What you'll need
- The Pluck CLI installed (
pnpm add -g @sizls/pluck-cli). - A robot autonomy stack capable of emitting signed triples. The package's library API (
createEmbodiedLedgerSystem,observeTriple) is designed for embedding in ROS (Robot Operating System) / MAVLink (the open MAV protocol used by drones) / Apollo (Baidu's autonomy stack) / Autoware (the open-source AV stack) middleware. - A Fingerprint-anchored hardware identity per agent (the per-die signing key the agent uses).
- A Celeste-anchored time source for chain-consistency checks.
Step-by-step
The alpha runs an in-memory demo on synthetic triples. Live capture (a ROS / MAVLink / Apollo / Autoware bridge feeding signed triples into Embodied-Ledger) is delivered as the library API today and as a daemon in a follow-up. To see the engine work today:
pluck bureau embodied-ledger demo
You'll see something like:
embodied-ledger/demo: ingesting signed triples (4 legit + 1 command-without-frame + 1 frame-replay)...
embodied-ledger/demo: tamper proofs emitted = 2
embodied-ledger/demo: proofId=a4b1c2… kind=command-without-frame agentId=delivery-drone-007
embodied-ledger/demo: proofId=33ee99… kind=frame-replay agentId=delivery-drone-007
embodied-ledger/demo: proofs notarized (stub) = 2
The synthesized run ingests six signed triples for a single drone – four legitimate 30 Hz cycles, one command emitted 1.5 seconds after the last frame (command-without-frame), one frame-digest re-used at a later instant (frame-replay). The constraint engine emits two tamper proofs.
Run it yourself
Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-embodied-ledger @sizls/pluck-bureau-core tsx):
// index.ts
import { createHash } from "node:crypto";
import {
createEmbodiedLedgerSystem,
fingerprintPrivateKey,
signCanonicalBody,
} from "@sizls/pluck-bureau-embodied-ledger";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";
const digest = (s: string) => createHash("sha256").update(s).digest("hex");
const flush = (n = 20) =>
new Promise<void>((res) => {
let i = 0;
const tick = () => (++i >= n ? res() : setImmediate(tick));
setImmediate(tick);
});
function buildTriple(body: any, key: string) {
const skel = { schemaVersion: 1, ...body };
const tripleId = createHash("sha256").update(JSON.stringify(skel)).digest("hex");
const signed = signCanonicalBody({ ...skel, tripleId }, key);
return { ...skel, tripleId, signature: signed.signature };
}
async function main() {
const operator = generateOperatorKey();
const agent = generateOperatorKey();
const agentKey = agent.privateKeyPem;
const agentFingerprint = fingerprintPrivateKey(agentKey);
const agentId = "delivery-drone-007";
const embodied = createEmbodiedLedgerSystem({
signingKey: operator.privateKeyPem,
disablePausePoll: true, disableLogging: true,
});
const envelope = {
maxAccelerationMps2: 5.0, maxTorqueNm: 50.0, maxSteeringRateRadPerSec: 1.5,
};
const safeCmd = { accelerationMps2: 1.2, torqueNm: 12.0, steeringRateRadPerSec: 0.3 };
const wm1 = digest("wm:plan-1");
const wm2 = digest("wm:plan-2");
try {
// 4 legit triples at 30Hz with shared world-model digests.
const cycles = [
["frame:1", wm1, "cmd:1", "00:00:00.000Z"],
["frame:2", wm1, "cmd:2", "00:00:00.033Z"],
["frame:3", wm2, "cmd:3", "00:00:00.066Z"],
["frame:4", wm2, "cmd:4", "00:00:00.099Z"],
];
for (const [f, wm, c, t] of cycles) {
embodied.observeTriple(buildTriple({
agentId, agentFingerprint,
sensorFrameDigest: digest(f), worldModelDigest: wm,
motorCommandDigest: digest(c), command: safeCmd, envelope,
observedAt: `2026-04-26T${t}`,
}, agentKey));
}
// command-without-frame – > 250ms gap from last frame.
embodied.observeTriple(buildTriple({
agentId, agentFingerprint,
sensorFrameDigest: digest("frame:gap"), worldModelDigest: wm2,
motorCommandDigest: digest("cmd:gap"), command: safeCmd, envelope,
observedAt: "2026-04-26T00:00:01.500Z",
}, agentKey));
// frame-replay – same sensorFrameDigest as triple #2 at later time.
embodied.observeTriple(buildTriple({
agentId, agentFingerprint,
sensorFrameDigest: digest("frame:2"), worldModelDigest: wm2,
motorCommandDigest: digest("cmd:replay"), command: safeCmd, envelope,
observedAt: "2026-04-26T00:00:01.600Z",
}, agentKey));
await flush();
const proofs = embodied.facts.tamperProofs();
console.log(`tamper proofs: ${proofs.length}`);
for (const p of proofs) console.log(` kind=${p.kind} agent=${p.agentId}`);
} finally {
await embodied.shutdown();
}
}
main().catch((err) => { console.error(err); process.exit(1); });
Run with tsx index.ts. Expected output:
tamper proofs: 2
kind=command-without-frame agent=delivery-drone-007
kind=frame-replay agent=delivery-drone-007
▶ Open in StackBlitz – runs in your browser, no install required.
What you get
A signed EmbodiedLedger.Tamper cassette per contradiction containing:
- The verbatim offending triples, each signed by the agent's hardware-anchored key.
- The contradiction kind and the OEM safety envelope (for out-of-envelope).
- A Rekor uuid pinning the bundle to the public log.
Raw sensor payloads never appear. Only sha256(canonical-frame-bytes). Operators keep the digest-to-bytes mapping in their local registry – the public predicate carries provenance, not content. Plaintiffs, regulators, and journalists can verify the bundle without trusting the manufacturer.
What it can't do
- Up to 20,000 live triples, 1,024 incident replays, and 1,024 tamper proofs in memory before FIFO eviction. Long-running fleets must persist.
- Embodied-Ledger does NOT detect a coordinated attack where the agent's signing key is itself compromised. If the attacker can sign arbitrary triples, the chain is internally consistent. Pair with Fingerprint (per-die hardware identity) and Oath key rotation to bound key-compromise blast radius.
- The agent decides what to sign. A manufacturer that simply doesn't run Embodied-Ledger has no triples to attest. Adoption is the operator-deployment property, not a property of the program.
- Live daemon mode ships in a follow-up. The library API is stable today.
A real-world example
A regional warehouse operator runs a fleet of 80 autonomous mobile robots. Six months after Embodied-Ledger goes live, a worker is struck and bruised by an AMR turning a corner. The operations team pulls the relevant agent's signed triples for the 30 seconds around the incident. Three triples in a row show command-without-frame: the robot's lidar pipeline had stalled (the perception-stack supervisor never reported the frame, the motion-planner kept commanding from the last cached world-model output). Embodied-Ledger had quietly emitted three signed command-without-frame proofs at the time. The operations team uses the bundle to file a software-defect report with the AMR vendor; the vendor uses the bundle to issue a fleet-wide patch and a recall notice on the perception-stack supervisor. The injured worker's attorney gets the same Rekor uuids the manufacturer's safety team got – both sides have the same observations.
For developers
Predicate URIs
| URI | What it attests |
|---|---|
https://pluck.run/EmbodiedLedger.Triple/v1 | One agent-signed (sensor-frame digest, world-model digest, motor-command digest) triple with command magnitudes and OEM envelope. |
https://pluck.run/EmbodiedLedger.Replay/v1 | One Bureau-assembled per-agent ordered triple chain plus its sha256-pairwise Merkle root. |
https://pluck.run/EmbodiedLedger.Tamper/v1 | One Bureau-signed tamper proof (command-without-frame, world-model-skip, out-of-envelope, or frame-replay) carrying verbatim offending triples. |
Programs composed
- Raven / Celeste – provide the time anchor the chain's
observedAtinstants are checked against - Fingerprint – provides the per-(agent, hardware) physical fingerprint pattern that pins which physical robot signed which triple
- Custody – provides per-agent chain-of-custody so a single agent's triples reach Rekor without operator-side cherry-picking
- Sigstore Rekor – proofs are notarized as DSSE in-toto envelopes
Threat model + adversary
20K live triples + 1K replays + 1K proofs in memory before FIFO eviction. Acceleration cap 1e6 m/s², torque cap 1e9 N·m, steering-rate cap 1e3 rad/s. Embodied-Ledger does NOT detect coordinated attacks where the agent's signing key is itself compromised – pair with Fingerprint + Oath rotation to bound blast radius. Raw sensor payloads never appear in a signed body – only sha256(canonical-frame-bytes).
Verify a published cassette
pluck bureau verify <bundle-dir>
cosign verify-blob \
--key <pubkey.pem> \
--signature <signature.sig> \
--type https://pluck.run/EmbodiedLedger.Tamper/v1 \
<body.json>
The package exports validateEmbodiedTamperProof, verifyCanonicalBody, and predicateTypeForTamperKind.
See also
- Bureau Foundations
- Threat Model
- Verify a dossier
- Celeste – GPS-anchored time witness
- Fingerprint – per-(device, hardware) physical fingerprinting
- Custody – per-device chain-of-custody
- Icarus – sibling drone command-and-control attestor