Audit Trail
Every sandbox has an HMAC-chained audit log that records lifecycle events (create, exec, hibernate, resume, destroy) with a chained hash so tampering is detectable. The chain is keyed with a per-team secret derived from the runner’s master HMAC key.
Read the log
from isorun import Sandbox
with Sandbox("python") as sb: sb.exec("echo hello") sb.exec("python3 -c 'print(2 + 2)'") log = sb.audit_log()
for entry in log: print(entry["seq"], entry["event"], entry.get("data"))# 1 sandbox.created {'image': 'python:3.12', 'vcpus': 1, 'mem_mib': 1024}# 2 exec.start {'command': 'echo hello'}# 3 exec.exit {'command': 'echo hello', 'exit_code': 0, 'duration_ms': 7}# 4 exec.start {'command': "python3 -c 'print(2 + 2)'"}# 5 exec.exit {...}# 6 sandbox.destroyed {'cpu_ms': 124, 'mem_peak_bytes': 84672512, ...}Each entry is a JSON object with at least:
{ "seq": 3, "ts": "2026-04-08T14:23:01.412Z", "sandbox_id": "run...", "event": "exec.exit", "data": { "command": "echo hello", "exit_code": 0 }, "hmac": "sha256:abc123..."}Verify the chain
The hmac field is HMAC-SHA256(team_secret, prev_hmac || seq || event || canonicalize(data)).
Tampering with any entry breaks the chain at that point and every
later entry. You can verify the chain client-side without round-tripping
to the runner — pull the team secret from your dashboard / vault
once and run the verifier locally.
A reference verifier ships with the SDK as isorun.audit.verify(log, team_secret). (Coming soon.)
What’s logged
| Event | When | Data |
|---|---|---|
sandbox.created | Create returns | image, vcpus, mem_mib, disk_mib |
sandbox.forked | A child is forked from this run | parent, image |
exec.start | Each sb.exec() / sh.run() | command |
exec.exit | Same call returns | command, exit_code, duration_ms |
sandbox.hibernated | sb.hibernate() returns | snapshot_size_bytes |
sandbox.resumed | sb.resume() returns | restore_ms |
sandbox.destroyed | sb.destroy() or auto-destroy | cpu_ms, mem_peak_bytes, uptime_ms |
The log is also flushed to R2 on destroy (best-effort, fire-and-forget) so you have a permanent audit trail even if the runner is later torn down.
Why HMAC and not just signed?
HMAC chains have two properties signed logs don’t:
- Append-only verification. Anyone with the team secret can verify any prefix of the log without the next entries. Useful for streaming verification during long-running sessions.
- Per-tenant isolation. Each team has its own derived secret, so a leaked verification key only compromises one team’s audit trail.
The chain is also append-only on the runner side — there’s no API to mutate or delete entries. The only way to break the chain is to tamper with the on-disk file, which you’d detect on the next verification.