Chronicle

HTTP Admin API

Complete reference for the Chronicle admin API — 21 endpoints.

The admin API is served by handler.New(deps, router). All routes are mounted under /v1. Requests and responses use JSON. All requests require an AppID in the context; missing AppID returns 401 Unauthorized.

Setup

import "github.com/xraph/chronicle/handler"

api := handler.New(handler.Dependencies{
    AuditStore:     myStore,
    VerifyStore:    myStore,
    ErasureStore:   myStore,
    RetentionStore: myStore,
    ReportStore:    myStore,
    Compliance:     complianceEngine,
    Retention:      retentionEnforcer,
    Logger:         slog.Default(),
}, mux)
api.RegisterRoutes(mux)

When using the Forge extension, routes are registered automatically unless WithDisableRoutes(true) is set.

Error format

{"error": "chronicle: event not found"}
CodeCondition
400Invalid request body or query parameters
401Missing AppID
404Entity not found
500Internal error

Events

List events

GET /v1/events

Query parameters:

ParameterTypeDescription
categorystringFilter by event category
actionstringFilter by action verb
severitystringinfo, warning, or critical
outcomestringsuccess, failure, or denied
fromRFC3339Start time
toRFC3339End time
limitintPage size (default 20)
offsetintPage offset
orderstringasc or desc (by timestamp)

Response 200 OK: audit.QueryResult

{
  "events": [
    {
      "id": "audit_01j9vk...",
      "timestamp": "2025-01-15T10:00:00Z",
      "sequence": 42,
      "hash": "abc123...",
      "prev_hash": "def456...",
      "stream_id": "stream_01j9vk...",
      "app_id": "myapp",
      "tenant_id": "tenant-1",
      "user_id": "user-42",
      "action": "login",
      "resource": "session",
      "category": "auth",
      "resource_id": "sess-001",
      "outcome": "success",
      "severity": "info"
    }
  ],
  "total": 1
}

Get event

GET /v1/events/:id

Response 200 OK: Single audit.Event.

Response 404 Not Found

Events by user

GET /v1/events/user/:userId

Query parameters: from, to (RFC3339), limit, offset.

Response 200 OK: audit.QueryResult

Aggregate events

POST /v1/events/aggregate

Request body: audit.AggregateQuery

{
  "group_by": "category",
  "from": "2025-01-01T00:00:00Z",
  "to": "2025-01-31T23:59:59Z"
}

Response 200 OK: audit.AggregateResult

{
  "buckets": [
    {"name": "auth", "count": 142},
    {"name": "billing", "count": 38}
  ]
}

Verification

Verify hash chain

POST /v1/verify

Request body:

{
  "stream_id": "stream_01j9vk...",
  "from_seq": 0,
  "to_seq": 0
}

from_seq and to_seq default to 0 (full chain). stream_id is optional if app_id and tenant_id are in context.

Response 200 OK: verify.Report

{
  "valid": true,
  "verified": 42,
  "gaps": [],
  "tampered": [],
  "first_event": 1,
  "last_event": 42
}

Erasure

Request erasure

POST /v1/erasures

Request body:

{
  "subject_id": "user-42",
  "reason": "GDPR Article 17",
  "requested_by": "dpo@company.com"
}

Response 201 Created: erasure.Result

{
  "id": "erasure_01j9vk...",
  "subject_id": "user-42",
  "events_affected": 5,
  "key_destroyed": true
}

List erasures

GET /v1/erasures

Query parameters: limit, offset.

Response 200 OK: []*erasure.Erasure

Get erasure

GET /v1/erasures/:id

Response 200 OK: erasure.Erasure

Response 404 Not Found


Retention

List policies

GET /v1/retention

Response 200 OK: []*retention.Policy

Save policy

POST /v1/retention

Request body:

{
  "category": "auth",
  "duration": "2160h",
  "archive": true
}

Response 201 Created: retention.Policy

Delete policy

DELETE /v1/retention/:id

Response 204 No Content

Enforce retention

Immediately runs all retention policies once.

POST /v1/retention/enforce

Response 200 OK: retention.EnforceResult

{
  "archived": 150,
  "purged": 150,
  "retained": 8420
}

List archives

GET /v1/retention/archives

Response 200 OK: []*retention.Archive


Reports

List reports

GET /v1/reports

Query parameters: limit, offset.

Response 200 OK: []*compliance.Report

Generate SOC2 report

POST /v1/reports/soc2

Request body: compliance.SOC2Input

{
  "period": {"from": "2025-01-01T00:00:00Z", "to": "2025-03-31T23:59:59Z"},
  "app_id": "myapp",
  "tenant_id": "tenant-1",
  "generated_by": "admin@company.com"
}

Response 201 Created: compliance.Report

Generate HIPAA report

POST /v1/reports/hipaa

Request body: compliance.HIPAAInput (same shape as SOC2Input).

Response 201 Created: compliance.Report

Generate EU AI Act report

POST /v1/reports/euaiact

Request body: compliance.EUAIActInput.

Response 201 Created: compliance.Report

Generate custom report

POST /v1/reports/custom

Request body: compliance.CustomInput

{
  "period": {"from": "2025-01-01T00:00:00Z", "to": "2025-01-31T23:59:59Z"},
  "app_id": "myapp",
  "title": "Q1 Security Review",
  "categories": ["auth", "billing"],
  "generated_by": "admin@company.com"
}

Response 201 Created: compliance.Report

Get report

GET /v1/reports/:id

Response 200 OK: compliance.Report

Response 404 Not Found

Export report

GET /v1/reports/:id/export/:format

:format is one of: json, csv, markdown, html.

Response 200 OK: Report content in the requested format (Content-Type varies).


Stats

Chronicle stats

GET /v1/stats

Response 200 OK:

{
  "total_events": 8420,
  "events_by_severity": {
    "info": 7800,
    "warning": 580,
    "critical": 40
  },
  "events_by_outcome": {
    "success": 8100,
    "failure": 250,
    "denied": 70
  }
}

On this page