Attestations
Attestations are graph-level audit nodes that summarize an AI agent session โ cost, token usage, model breakdown, code change statistics, and which changes are covered. They are created automatically when a session ends and travel with the changes they cover on push.
What Is an Attestation?โ
When an agent session ends (the user closes the conversation, or the session is deleted), Atomic creates an attestation covering all the changes recorded during that session. The attestation aggregates data from the provenance entries embedded in each covered change:
Session: agent-ses_3781fc7a6ffet5c6r1ILy1BEbv
โ
โโโ Turn 1: Change ABC123 (provenance: claude-sonnet-4-5, 3.2k tokens, $0.04)
โโโ Turn 2: Change DEF456 (provenance: claude-sonnet-4-5, 5.1k tokens, $0.06)
โโโ Turn 3: Change GHI789 (provenance: claude-sonnet-4-5, 4.1k tokens, $0.05)
โ
โผ
Attestation XMJZ3IPF
โโโ Agent: OpenCode (anthropic)
โโโ Session: agent-ses_3781fc...
โโโ Models: claude-sonnet-4-5 (12.4k tokens, $0.15)
โโโ Code: +116 lines, -8 lines
โโโ Wall time: 3m 42s
โโโ Changes covered: ABC123, DEF456, GHI789
Viewing Attestationsโ
List All Attestationsโ
$ atomic agent attest
XMJZ3IPF OpenCode ยท claude-sonnet-4-5 ยท 12.4k tokens ยท 3m 42s ยท 3 changes
R3KQP7YN Claude Code ยท claude-sonnet-4-5 ยท 8.1k tokens ยท 1m 15s ยท 1 change
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Total: $0.27 ยท 4 changes covered ยท 20.5k tokens
Inspect a Specific Attestationโ
$ atomic agent attest --hash XMJZ3IPF
Attestation XMJZ3IPF
Agent: OpenCode
Session: agent-ses_3781fc7a6ffet5c6r1ILy1BEbv
Changes: 3 changes
Wall time: 3m 42s
Cost: $0.15
Tokens: 12.4k
Code: +116 -8
Model Breakdown:
claude-sonnet-4-5: 3.2k in / 9.2k out ยท $0.15
Changes Covered (3):
ABC12345
DEF45678
GHI78901
Coverage:
dev โโโโโโโโโโโโโโโโโโโโ 3/5 (60%)
Filter by Stackโ
$ atomic agent attest --stack dev
Verbose Outputโ
$ atomic agent attest --verbose
Shows per-model token breakdown and per-change details for every attestation.
How Attestations Are Createdโ
Automatic Creation at Session Endโ
When the TurnOrchestrator receives a session-end event and the session had at least one turn:
- Query the agent stack history โ get all change hashes on the agent's stack
- Check for existing attestations โ find which changes are already covered by prior attestations from the same session (for resumed sessions)
- Determine new changes โ filter to changes not yet attested
- Load each change โ read provenance entries (model, tokens, cost) and file operations (lines added/removed)
- Aggregate data:
- Per-model token and cost totals
- Total lines added and removed from the CRDT semantic layer
- Wall duration from session timestamps
- Build and save โ create the attestation with all aggregated data
Data Sourcesโ
The attestation pulls data from two places:
| Data | Source | How It Gets There |
|---|---|---|
| Model name | change.provenance[].model | Set by build_turn_provenance() at record time |
| Token counts | change.provenance[].tokens | Set by build_turn_provenance() at record time |
| Cost | change.provenance[].cost | Set by build_turn_provenance() at record time |
| Lines added/removed | change.file_ops[].line_ops[] | Generated by the CRDT semantic layer during recording |
| Wall duration | session.started_at / session.ended_at | Tracked by the TurnOrchestrator |
| Agent identity | session.agent_name / session.agent_vendor | Set from hook events (OpenCode sends provider/model) |
Fallback Behaviorโ
If no provenance data is found in the changes (e.g., changes recorded without the agent hooks), but the session knows the model name, the attestation still creates a minimal model entry. This ensures the attestation always names the model, even when token/cost data is unavailable.
Resumed Sessionsโ
When an agent session is resumed (e.g., claude --resume or continuing an OpenCode session), the attestation system handles it correctly:
- Finds existing attestations for this session ID
- Determines which changes are new (not yet covered)
- Creates a new attestation covering only the new changes
- Chains to the previous attestation via the
previous_attestationfield
$ atomic agent attest --hash R3KQP7YN
Attestation R3KQP7YN
Agent: Claude Code
Previous: XMJZ3IPF # โ chains to the prior attestation
Notes: Resumed session (1 new change, 4 total in session)
...
This ensures every change is covered by exactly one attestation, with a clear chain showing the session's history.
On-Demand Generationโ
The server API also supports generating attestations on demand from the provenance data in changes:
POST /tenant/:id/portfolio/:id/project/:id/attestations/generate
This reads the provenance entries from every change on a stack, aggregates them, and creates an attestation. This is useful when:
- Changes were pushed before the attestation was created locally
- You want to regenerate an attestation with updated data
- The local attestation was lost or corrupted
Storage and Transportโ
On Diskโ
Attestations are stored in the same two-level directory structure as changes:
.atomic/changes/
โโโ AB/
โ โโโ ABCDEF1234567890.change # A change
โ โโโ ABCDEF1234567890.attest # An attestation
โโโ XM/
โโโ XMJZ3IPF...........attest # Another attestation
Content Addressingโ
Like all Atomic artifacts, attestations are content-addressed:
data = postcard::serialize(attestation)
hash = blake3(MAGIC_BYTES + data)
path = .atomic/changes/{hash[0:2]}/{hash}.attest
Pushโ
When you push changes, Atomic automatically uploads attestations that cover the pushed changes:
$ atomic push origin
โ Pushed 3 changes
โ XMJZ3IPF attestation ($0.15, 3 covered)
An attestation is only uploaded when all of its covered changes have been pushed. This ensures the server never has an attestation referencing changes it doesn't have.
Data Modelโ
Attestationโ
| Field | Type | Description |
|---|---|---|
version | u8 | Schema version for forward compatibility |
timestamp | i64 | Unix epoch seconds when created |
agent | AttestAgent | Agent identity (name, display name, vendor) |
session_id | String | Session this attestation covers |
cost_usd | f64 | Total cost across all models |
duration_api_ms | u64 | API processing time |
duration_wall_ms | u64 | Wall clock duration of the session |
code_changes | CodeChangeStats | Lines added and removed |
models | Vec<ModelUsage> | Per-model token and cost breakdown |
changes_covered | Vec<Hash> | Hashes of changes this attestation covers |
previous_attestation | Option<Hash> | For resumed sessions โ chains to prior attestation |
notes | Option<String> | Human-readable context |
ModelUsageโ
| Field | Type | Description |
|---|---|---|
model | String | Model identifier (e.g., claude-sonnet-4-5) |
input_tokens | u64 | Prompt/input tokens |
output_tokens | u64 | Completion/output tokens |
cache_read_tokens | u64 | Tokens read from cache |
cache_write_tokens | u64 | Tokens written to cache |
cost_usd | f64 | Cost for this model's usage |
CodeChangeStatsโ
| Field | Type | Description |
|---|---|---|
lines_added | u64 | Total lines added across all covered changes |
lines_removed | u64 | Total lines removed across all covered changes |
Web UIโ
The Atomic web UI renders attestations on the Attestations tab of each project. The display includes:
- Summary card โ Agent name, cost, tokens, duration, change count, lines changed
- Models Used โ Per-model breakdown with token counts and cost
- Provenance Graphs โ Interactive visualization of the session's decision DAG (from the provenance graphs associated with covered changes)
- Timeline โ Temporal view of the session's activity
See Alsoโ
- Provenance Graphs โ How agent reasoning is captured as causal DAGs
- Agent Integration Overview โ How the full agent lifecycle works
atomic agent attestcommand reference โ CLI documentation