Record not “what happened” but “why it moved that way”
A transport robot moves unexpectedly and stops. What you are then asked to explain comes down to four things: what the sensors were seeing, what the model proposed, who or which component authorized it, and how the robot actually moved.
However many logs and sensor recordings you have kept, if those four are not connected, they cannot be followed as a single line. The purpose of an audit log is to leave that line behind.
Debug logs, sensor recordings, and audit logs serve different purposes
Taking ROS 2 as the example makes the difference clear.
Debug logs. ROS 2 logging is a mechanism for emitting messages with severities from DEBUG to FATAL to the console, to log files on disk, and to a topic on the network, with the targets switchable on a per-node basis. The content is the developer’s diagnostic messages, and what gets emitted is up to whoever implemented it.
Sensor recordings. rosbag2 is a tool for storing the data that flowed over topics and services so that it can be replayed later for reproduction and analysis.
Audit logs. The difference is not the medium but the purpose and what is recorded. Diagnostic messages and sensor values alone do not tell you automatically why a decision was made or who approved it. You can use ROS 2 logging or rosbag to carry audit records, but even then you need an implementation that emits decisions and approvals as structured events. An audit log is a record kept in order to trace decisions and causality: predetermined events, recorded with predetermined fields.
The fields follow from the questions you want to answer
Rather than recording everything all the time, you pick the rows according to the questions you want to trace.
| Field | Purpose | Example |
|---|---|---|
| task id / trace id | Groups the events belonging to one task or instruction | One transport job, one pick |
| ref_event (referenced event ID) | States which event this one followed from, so causality can be traced | The ID of the proposal event that an authorization event refers to |
| boot / session id | Distinguishes records across restarts and reconnections | An identifier that changes with each boot |
| event id and seq | Confirms gaps and ordering through a per-source sequence number | A monotonically increasing number per node |
| Event time and clock-sync status | Keeps wall-clock time, monotonic time, and sync offset separately | UTC, time since boot, NTP/PTP offset |
| Collection time | Keeps the time the recording side received the event separate from the time it occurred | Receipt time at the collection server |
| actor | Who, which component, or which model acted | The perception node, the planning model, the supervisor’s ID |
| model / config / policy version | Identifies afterwards which version made the decision | Model hash, configuration file version, authorization rule version |
| Input reference | Keeps the input a decision was based on as a reference rather than the data itself | Frame ID, image hash, position within a bag |
| Proposal (action target and parameters) | What the model was about to do | Target position, speed, grip force |
| Decision (decision and reason) | Authorize, reject, or modify, with the reason and the rule applied | “Modified because it exceeded the speed limit” |
| Execution result (result) | The command actually sent, and feedback from the robot | Speed sent, position reached, reason for stopping |
For the vocabulary of the structure, the OpenTelemetry logs data model is a useful reference. That model keeps the time an event occurred and the time the collection system observed it as separate fields, ties related events together with a trace ID and a span ID, and defines a normalized severity and attributes. The fields listed here are our design example, informed by that model; they are not an official standard format.
A fictional event example
A transport robot proposes moving to a target position, the verification layer modifies the speed and authorizes it, the command is sent, and some time later the result arrives. Written as four events, that flow looks like this. Fields that repeat in every event, such as clock-sync status, are omitted.
[
{"trace_id":"job-4821","boot_id":"b-0f31","event_id":"plan-17","seq":17,"source":"planner",
"t_wall":"2026-09-21T03:12:08.412Z","t_mono_ns":8123456789012,
"actor":{"type":"model","name":"nav-planner","version":"sha256:9f3c…"},
"input_ref":{"frame_id":"cam0-77213","hash":"sha256:1a8e…"},
"proposal":{"action":"move_to","target":"dock-B","speed_mps":1.4}},
{"trace_id":"job-4821","boot_id":"b-0f31","event_id":"gate-9","seq":9,"source":"action-gate",
"t_wall":"2026-09-21T03:12:08.419Z","t_mono_ns":8123463901120,"ref_event":"plan-17",
"actor":{"type":"policy","name":"action-gate","policy_version":"v12"},
"decision":"modify","reason":"speed_limit_zone_C","result_params":{"speed_mps":0.6}},
{"trace_id":"job-4821","boot_id":"b-0f31","event_id":"exec-31","seq":31,"source":"base-controller",
"t_wall":"2026-09-21T03:12:08.430Z","t_mono_ns":8123474550301,"ref_event":"gate-9",
"command_sent":{"action":"move_to","target":"dock-B","speed_mps":0.6}},
{"trace_id":"job-4821","boot_id":"b-0f31","event_id":"exec-32","seq":32,"source":"base-controller",
"t_wall":"2026-09-21T03:12:41.902Z","t_mono_ns":8156946550301,"ref_event":"exec-31",
"result":{"status":"reached","position":"dock-B","motion":"stopped"}}
]
The proposal, the authorization (here a modification), the sending of the command, and the arrival of the result are each appended as a separate event. From the second event onward, ref_event points at the preceding event, and the fact that something was authorized, the fact that a command was sent, and its result stay separate facts. This is an illustrative format, not a particular standard implementation.
Time alone does not establish order and causality
In a system spread across several computers and microcontrollers, wall clocks drift, and even combining timestamps with sequence numbers does not settle causality. Causality is traced through explicit referenced event IDs such as ref_event. Per-source sequence numbers and monotonic time are there to establish ordering within that source and to flag candidate gaps; wall-clock time and the recorded sync status are there to assist comparison between devices.
Records that span restarts and reconnections are distinguished by boot / session id, and events whose order is unknown are left unknown rather than reordered on a guess. OWASP’s logging guidance likewise recommends time synchronization between devices while offering the alternative of recording offsets and confidence where synchronization is impractical.
Protection and operations
An audit log is itself a target of attack and a subject of incident investigation. Decide the following at the design stage.
- Tamper detection. A simple per-record hash means nothing if the hash can be rewritten too. A chain that includes the previous record’s hash can also be recomputed wholesale or truncated at the end, so the chain’s verification value should be anchored periodically in a separately administered location. If you use signatures, protect the keys off the robot, and at the external store separate the rights to modify and delete from the operators. Getting the format right does not guarantee authenticity, and these are detection of tampering, not a guarantee of prevention.
- Separation of access. Keep the parties who can read and write the logs separate from those who operate the robot. Record access to the logs itself.
- Storage and transfer. Encrypt the path from temporary storage on the robot through to transfer outside, and make storage append-only after transfer.
- Gap detection. Detect missing records through jumps in sequence numbers and a periodic heartbeat.
- Logging stoppages and running out of capacity. Decide per use, in advance, whether operation continues, stops, or merely raises a notification while recording is impossible. Design so that waiting on recording or transfer does not block the control loop indefinitely, and verify the latency and load of the recording path and the behavior on loss ahead of time. The state you least want is one in which nobody notices that recording has stopped.
- What not to record. Do not record credentials or keys, or personal information you do not need. Keep images containing people’s faces as a reference and a hash rather than the original image, limit retention of the original to defined conditions, and set access rights and a retention period for the referenced location as well. Decide the retention period from the purposes of investigation, explanation, and revalidation, and from the requirements that apply to your organization.
Where to start
The first thing to decide is the questions you want to trace. Make them concrete, such as “why did it stop at this position,” “why did it move at this speed,” and “who authorized it,” and narrow them to about three. Once the questions are set, so are the events you need and the rows to select from the table above.
Next, decide the policy for time and IDs: how trace ids are assigned, which sources issue sequence numbers, and how sync status is recorded. Then decide the transfer destination, where signing keys are held, the behavior when recording is impossible, and the retention period.
The reason for deciding these first is simple: decisions and approvals that were not recorded at the time cannot be confirmed after the fact. Fields will be added as operations change, but an added field never appears in past events.
With these records in place, you can isolate where among input, proposal, decision, and execution things differed from expectation, compare the same fields before and after a model or configuration change, and keep the basis for explaining and revalidating to an outside party. An audit log does not, on the other hand, guarantee complete reconstruction of an event or the prevention of an accident. Reconstruction needs sensor recordings, and prevention needs design and safety mechanisms, each separately.
Talk to us
Adding records after something has gone wrong does not restore decisions and approvals that were not kept at the time. In our custom AI development, audit log design is built into the development process, so that you can trace what happened and where in perception, decision, and control it happened. To discuss adding audit logs to an existing system, or how to decide which fields to record, use the contact form.
Reference materials
- Open Robotics, Logging and logger configuration, ROS 2 Documentation (Jazzy). Checked September 21, 2026.
- Open Robotics, Recording and playing back data, ROS 2 Documentation (Jazzy). Checked September 21, 2026.
- OpenTelemetry, Logs Data Model. Checked September 21, 2026.
- OWASP, Logging Cheat Sheet. Checked September 21, 2026.
The fields, the event example, and the approach to protection and operations are U-Rec, Inc.’s design examples, informed by the sources above. They do not indicate an official standard format or conformance to any particular legal requirement.