Operations

Audit migration runbook

F0.2 — hash-chained audit log · Last updated: 2026-04-30

fastpace audit migrate — runbook

Pre-0.5.0 fastpace installs wrote fastpace/audit.log as plain JSONL — one tool call per line, no chain links, no signatures. From 0.5.0 onward we ship the F0.2 hash-chained signed audit log. This runbook walks you through migrating the legacy file to the chain format without losing any history.

If you're on 0.5.0 or later from day one, you don't need this — your audit log was born in chain format. You can confirm with:

$ fastpace verify
[ok] chain integrity: N entries

When to run it

Run fastpace audit migrate once, after upgrading to 0.5.0+, if and only if fastpace verify reports a "legacy_format" status. The command is a no-op when the log is already a chain.

$ fastpace verify
[err] legacy_format detected at line 1: missing entry_hash
  run `fastpace audit migrate` to upgrade

What the migration does

  1. Reads every line of fastpace/audit.log in order.
  2. For each entry, computes: - seq — monotonically increasing from 0 - prev_hash — the previous entry's entry_hash (or the genesis hash 0000… for the first entry) - entry_hashsha256(canonical_json(body)) over a body that includes seq + prev_hash but excludes entry_hash + signature - signature — Ed25519 over entry_hash using the F0.1 install identity, if an identity exists. If you never ran fastpace identity show before migrating, the migration writes unsigned entries. They still chain-verify; they just don't attribute to a specific install identity.
  3. Writes the new chain-format file at fastpace/audit.log.
  4. Backs the original up to fastpace/audit.log.audit-migrated-<TIMESTAMP>.bak.jsonlnever deletes. You can compare with diff or wc -l afterwards.

Pre-flight checklist

Before running:

The migration command

$ fastpace audit migrate
[ok] migrated 1247 entries into the chain format.
  backup      fastpace/audit.log.audit-migrated-2026-04-30T14-21-08-921Z.bak.jsonl
  signed      yes

  Run `fastpace verify` to confirm the migrated chain is intact.

JSON-output mode (for CI / scripting):

$ fastpace audit migrate --json
{
  "ok": true,
  "migrated": 1247,
  "signed": true,
  "path": "/repo/fastpace/audit.log",
  "backup_path": "/repo/fastpace/audit.log.audit-migrated-2026-04-30T14-21-08-921Z.bak.jsonl"
}

Verify after migrating

$ fastpace verify
[ok] chain integrity: 1247 entries
  signatures verified  1247

If fastpace verify reports chain BROKEN, don't touch anything — your .bak.jsonl file is the source of truth. Run with debug logging to capture the failure context:

$ FASTPACE_DEBUG=1 fastpace verify > verify.log 2>&1

…and email verify.log to security@fastpace.net. Migration is deterministic — if it broke once on your inputs, it'll break the same way on ours.

Restoring from backup

If you need to roll back the migration entirely (e.g. a downstream tool only understands the legacy format):

$ cp fastpace/audit.log.audit-migrated-<TS>.bak.jsonl fastpace/audit.log

You can keep the backup file in the repo — it's gitignored by default via the fastpace/.audit-migrated-* rule. If you want to permanently discard it once you're confident in the migration:

$ rm fastpace/audit.log.audit-migrated-*.bak.jsonl

Edge cases

The legacy log was empty

Migration is a no-op; the chain-format file is written with zero entries. fastpace verify reports 0 entries and passes.

The legacy log has a malformed line

Migration stops at the first parse error and leaves the original file untouched. The error report identifies the line:

[err] migrate: line 412 parse failed: Unexpected token in JSON
  the original file is unchanged at fastpace/audit.log

Open the offending line, fix it (typical cause: a partial write from a crash mid-append), then re-run fastpace audit migrate.

You ran the migration twice

Idempotent — once the file is chain-format, the second run reports:

$ fastpace audit migrate
[ok] audit log is already in chain format — nothing to migrate.

You want to migrate a subset

Not supported. Migration walks the full file in order; the chain has to be contiguous from seq=0 for fastpace verify to pass.

What the migration doesn't do

Reporting an issue

security@fastpace.net. Include the verify output and the first 5 lines of the failing legacy log (redact prompt content; we only need the structural fields).