---
title: "Rotate the audit trail"
method: POST
path: "/v1/audit/tools/rotate"
tags: ["System"]
---

# Rotate the audit trail

`POST /v1/audit/tools/rotate`

## Request

- Base URL: `http://localhost:9998`
- URL: `http://localhost:9998/v1/audit/tools/rotate`
- Auth: HTTP bearer

Retires the current trail into an immutable read-only segment and opens a fresh one, preserving every byte — never renumbering, re-hashing or repairing. The only way out of a forked history, and never automatic. The request must name the trail identity, content digest and byte count it believes it is retiring (read them from the diagnostic export); a mismatch is refused with nothing touched. A repeated intentId replays the recorded receipt instead of rotating again. The permanent per-day rollup survives. Free at every tier, because a forked trail has stopped recording and this is what resumes it; refused for unauthenticated loopback callers. Requires system:control scope.

## Request body

- ToolAuditRotateRequest
  - `expectedTrailId` string, required: The trail the caller believes it is retiring.
  - `expectedContentDigest` string, required: SHA-256 hex over the whole records file, as last read from the diagnostic export.
  - `expectedByteCount` integer, required: Length of that same read.
  - `intentId` string, required: Caller-minted and single-use; a repeat replays the recorded receipt.

## Response `200`

Rotation receipt (rotated=false when a recorded intent was replayed)

- ToolAuditRotateReceipt
  - `predecessorTrailId` string, required
  - `segmentFileName` string, required: The immutable read-only segment the retired bytes now live in.
  - `segmentDigest` string, required: SHA-256 over the archived bytes, verified by reading the archive back.
  - `segmentByteCount` integer, required
  - `trailId` string, required: The fresh cursor space; a collector detects the rotation by this changing.
  - `intentId` string, required
  - `rotated` boolean, required: False when this call replayed an already-recorded intent.
  - `preservedRollupRows` integer, required: Permanent per-day rows carried across the rotation.

## Other responses

- `400`: Malformed body, empty identity, or a digest that is not 64 hex characters (invalid_request)
- `403`: Refused for an unauthenticated or unidentified caller (principal_forbidden)
- `409`: The trail on disk is not the one the request described; nothing was rotated (audit_rotation_precondition)
- `503`: Audit writer unavailable or the rotation did not complete (audit_unavailable)

---

[API](https://skmtc.dev/modelpiper/apis/toolpiper-api.md) · [All operations](https://skmtc.dev/modelpiper/apis/toolpiper-api/llms.txt) · [OpenAPI document](https://skmtc.dev/modelpiper/apis/toolpiper-api/revisions/6722bf56385c?raw)
