Freeze deployment, environment, robot, and signing-key identifiers in the preregistration.
deploymentId · environmentId · robotIdKeep the native controller. WANTED adds one narrow evidence layer: six event helpers, a hardware-backed Ed25519 signer, and a durable sink. The adapter serializes concurrent calls, assigns sequence numbers, signs RFC 8785 canonical bytes, and chains every accepted event.
The adapter needs identity, a signing callback, and an accepted-event sink. Sequence and chain state advance only after the sink succeeds.
Freeze deployment, environment, robot, and signing-key identifiers in the preregistration.
deploymentId · environmentId · robotIdPass canonical bytes to a TPM, HSM, secure enclave, or equivalent non-exportable key.
sign(bytes) → signaturePersist or POST each accepted event, then durably store the returned restart checkpoint.
sink(event) → acceptedThe production signer is deliberately outside the SDK. This keeps private key handling inside infrastructure already approved by the robot operator.
import { WantedClient, createHttpSink } from "./wanted-sdk.mjs";
const wanted = new WantedClient({
deploymentId: "dep_7f2",
environmentId: "env_104",
robotId: "robot_07",
signingKeyId: "key_prod_07",
sign: bytes => secureModule.sign(bytes),
sink: createHttpSink("https://collector.example/v1/events"),
checkpoint: await durableStore.load()
});
await wanted.intervention(
"remote_guidance", 43, "task_recovery", {
actor_role: "operator",
person_count: 1,
resolution: "robot_resumed",
support_session_sha256: "…"
}
);
await durableStore.save(wanted.checkpoint());Native ROS 2 topics, simulator callbacks, task planners, and operator consoles map into the same six calls.
wanted.lifecycle("activation", { participant_acceptance_ref: "controlled://acceptance/42", activation_record_sha256: "…" })wanted.state("available", { autonomous_service_capable: true })wanted.request("privacy", { evidence_ref: "local://request/42" })wanted.action("put mug in dishwasher", { proactive: false })wanted.intervention("remote_guidance", 43, "task_recovery", { person_count: 1, resolution: "robot_resumed" })wanted.incident("L1", "Brief hallway obstruction")A benchmark this long cannot depend on process memory. Persist the checkpoint after every accepted event and test crash recovery before human exposure.
Only one process may issue the next sequence for a deployment. Fail over with a lease or fencing token.
The SDK advances sequence and chain state only after the sink acknowledges the event.
Store next_sequence, previous_event_hash, and last_occurred_at before another event can be emitted.
After a crash, recover the collector’s accepted tail before emitting. Event IDs make retries idempotent.