Decision Trail — Inference Audit Log
Every inference decision your agent makes is logged — which model, what query, which facts were retrieved (with similarity scores), what the model output, how many tokens were used, and how long it took.
Each record gets a BLAKE2b-128 content-addressed ID and an optional Ed25519 signature for tamper-proof provenance.
Decision record schema
| Field | Type | Description |
|---|---|---|
decision_id | string | BLAKE2b-128 hex digest (deterministic) |
tenant_id | string | Tenant that owns the decision |
store_id | string | Memory store queried |
session_id | string | Session identifier (optional) |
model_id | string | LLM model used (e.g. gpt-4o) |
query | string | The user's query |
retrieved_refs | int[] | Memory fact refs that were retrieved |
retrieved_contents | string[] | Content of each retrieved fact |
retrieved_scores | float[] | Similarity scores |
output | string | Model's generated output |
token_count | int | Tokens consumed |
latency_ms | float | End-to-end latency in milliseconds |
timestamp | datetime | ISO-8601 with microsecond precision |
signature | bytes | Ed25519 signature (optional) |
public_key | bytes | Signer's public key (optional) |
Provenance
The decision_id is computed deterministically:
decision_id = BLAKE2b-128(tenant_id ‖ store_id ‖ model_id ‖ query ‖ timestamp)
When a signing key is configured, the decision is Ed25519-signed over the ID. This creates an immutable, verifiable chain of inference evidence.
API reference
POST /v1/decisions/log
Log a new decision record.
curl -X POST https://cloud.grafomem.com/v1/decisions/log \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"store_id": "default",
"model_id": "gpt-4o",
"query": "Where does Aria live?",
"retrieved_refs": [42, 87],
"retrieved_contents": ["Aria lives in Rome", "Aria works at ACME"],
"retrieved_scores": [0.95, 0.72],
"output": "Based on the memory, Aria lives in Rome.",
"token_count": 145,
"latency_ms": 230.5
}'
GET /v1/decisions/
List decisions with pagination and filtering.
| Parameter | Type | Description |
|---|---|---|
model_id | string | Filter by model |
store_id | string | Filter by store |
session_id | string | Filter by session |
limit | int | Page size (default 20, max 200) |
offset | int | Pagination offset |
GET /v1/decisions/stats
Summary statistics: total decisions, models used, average latency, total tokens.
GET /v1/decisions/{decision_id}
Get a single decision record by ID.
GET /v1/decisions/{decision_id}/replay
Replay a decision — returns which facts have been deleted since the decision was made.
{
"decision": { ... },
"facts_since_deleted": [42]
}
The replay endpoint is critical for GDPR audit trails. It answers: "If this decision were made today, would it have the same inputs?" If facts have been deleted since, the answer is no — and the erasure certificate proves the deletion.
GET /v1/decisions/export
Bulk export all decisions as NDJSON (newline-delimited JSON). Opens as a file download.
DELETE /v1/decisions/scrub/{fact_ref}
GDPR scrub — removes all references to a deleted fact from decision records. Content is replaced with [REDACTED — GDPR Article 17].
Portal UI
The Decision Trail tab in the Cloud Portal provides:
- Stats grid — total decisions, models used, avg latency, total tokens
- Filter bar — search by model, store, session
- Decision table — paginated list with time, model, query preview, facts count, tokens, latency, signed status
- Detail panel — click any row to see full query, retrieved facts with scores, model output
- Provenance badge — green badge showing Ed25519 signature + public key
- Replay — see which facts have been deleted since the decision
- NDJSON export — one-click bulk download