Documentation
¶
Overview ¶
Package handler wires HTTP routes for the Transparency Log API.
Paths match the reference TL swagger byte-for-byte. Operator/infra-only endpoints (health, ready, docs) are ans conventions with no reference counterpart.
Reference routes (mirrored exactly):
POST /v1/internal/agents/event — append a signed event (RA only)
GET /v1/agents/{agentId} — badge (real-time status)
GET /v1/agents/{agentId}/audit — paginated event history
GET /v1/agents/{agentId}/receipt — SCITT COSE_Sign1 receipt
GET /v1/agents/{agentId}/status-token — COSE_Sign1 status token
GET /v1/log/checkpoint — JSON CheckpointResponse
GET /v1/log/checkpoint/history — paginated checkpoint history
GET /v1/log/schema/{version} — JSON-Schema for TL events
GET /checkpoint — raw sumdb note (tlog-tiles)
GET /root-keys — verifier keys (sumdb-note fmt)
GET /tile/{level}/{index} — complete Merkle tile
GET /tile/{level}/{index}.p/{width} — partial Merkle tile
GET /tile/entries/{index} — complete entry tile
GET /tile/entries/{index}.p/{width} — partial entry tile
Operator-only (ans additions, not in reference swagger):
GET /v2/admin/health — liveness GET /v2/admin/ready — readiness GET /docs — Swagger UI GET /docs/openapi.yaml — raw spec
Index ¶
- type AdminHandlers
- func (h *AdminHandlers) CreateKey(w http.ResponseWriter, r *http.Request)
- func (h *AdminHandlers) GetByKeyID(w http.ResponseWriter, r *http.Request)
- func (h *AdminHandlers) ListByRAID(w http.ResponseWriter, r *http.Request)
- func (h *AdminHandlers) Mount(r chi.Router)
- func (h *AdminHandlers) RevokeKey(w http.ResponseWriter, r *http.Request)
- type Handlers
- func (h *Handlers) AppendEventIdentity(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) AppendEventV1(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) AppendEventV2(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetAgentIdentities(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetAgentIdentityHistory(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetAudit(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetBadge(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetCheckpointHistory(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetCheckpointJSON(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetIdentityAgents(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetIdentityAudit(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetIdentityBadge(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetIdentityReceipt(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetReceipt(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetRootKeys(w http.ResponseWriter, _ *http.Request)
- func (h *Handlers) GetSchema(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) GetStatusToken(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) Mount(r chi.Router, tileRoot string)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AdminHandlers ¶
type AdminHandlers struct {
// contains filtered or unexported fields
}
AdminHandlers holds the admin-route HTTP handlers for the TL. Currently wraps only the producer-key CRUD surface; as more admin routes land they'll hang off this same type so the wiring in main.go has a single injection point for "everything that needs admin".
func NewAdminHandlers ¶
func NewAdminHandlers(keys producerkey.AdminStore) *AdminHandlers
NewAdminHandlers constructs an AdminHandlers group over the given admin store. Passing the interface (not the concrete SQLite type) keeps the handler swappable — a future in-memory admin store for e2e tests, or a remote-API-proxy store, slots in without handler changes.
func (*AdminHandlers) CreateKey ¶
func (h *AdminHandlers) CreateKey(w http.ResponseWriter, r *http.Request)
CreateKey handles POST /internal/v1/producer-keys.
Response codes mirror the reference swagger §742-776:
200 OK — key registered, body is ProducerKeyResponse 409 Conflict — key_id already exists 422 Unprocessable — malformed body, invalid PEM, invalid date range
The reference spec uses 200 (not 201) on create; we follow.
func (*AdminHandlers) GetByKeyID ¶
func (h *AdminHandlers) GetByKeyID(w http.ResponseWriter, r *http.Request)
GetByKeyID handles GET /internal/v1/producer-keys/{key_id} — an ans-only extension not in the reference swagger (admins typically list-by-ra then filter client-side). We keep it because it makes the demo script and admin CLIs cheaper, and maps naturally to the existing store method.
func (*AdminHandlers) ListByRAID ¶
func (h *AdminHandlers) ListByRAID(w http.ResponseWriter, r *http.Request)
ListByRAID handles GET /internal/v1/producer-keys/ra/{ra_id}.
Reference returns 404 when no keys exist for the ra_id (§874); we match that behavior. An empty list suggests a typo; returning 200 with an empty array would make such typos silent.
func (*AdminHandlers) Mount ¶
func (h *AdminHandlers) Mount(r chi.Router)
Mount registers the admin producer-key routes under /internal/v1/producer-keys — matching the reference TL's swagger.yaml §720 byte-for-byte. Admin routes are intentionally not in the V2 RA spec (they're internal plumbing, not the client-facing API), so they keep the reference's shape. Callers are responsible for wrapping the router with the RequireAdmin middleware before or during Mount — this handler does no authentication/authorization itself.
Routes:
POST /internal/v1/producer-keys — register a new key
DELETE /internal/v1/producer-keys/{key_id} — revoke
GET /internal/v1/producer-keys/ra/{ra_id} — list keys for an RA
GET /internal/v1/producer-keys/{key_id} — fetch one by key_id (ans extension)
The last route is an ans-only addition used by admin tooling for single-record lookups; the reference does not expose it. Callers can achieve the same by listing by ra_id and filtering, but a direct-lookup route is cheaper when the caller already has the key_id in hand (e.g., after a CREATE).
func (*AdminHandlers) RevokeKey ¶
func (h *AdminHandlers) RevokeKey(w http.ResponseWriter, r *http.Request)
RevokeKey handles DELETE /internal/v1/producer-keys/{key_id}.
Returns 204 No Content on success, 404 if the key doesn't exist or is already revoked — matching the reference's behavior (swagger §800-820). Repeat DELETEs returning 404 (rather than idempotent 204) is intentional: the typo guardrail is worth the slight inconvenience.
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers groups the TL HTTP handlers.
func NewHandlers ¶
func NewHandlers( log *service.LogService, badge *service.BadgeService, identityBadge *service.IdentityBadgeService, receipt *service.ReceiptService, statusToken *service.StatusTokenService, checkpoint *service.CheckpointService, schema *service.SchemaService, rootKeysBody []byte, ) *Handlers
NewHandlers constructs a Handlers group.
rootKeysBody is the text/plain response body served at /root-keys — typically one sumdb-note verification line per verifier key concatenated with '\n'.
statusToken may be nil; when it is, the /status-token route returns 501 Not Implemented (mirroring reference swagger §305).
func (*Handlers) AppendEventIdentity ¶ added in v0.1.6
func (h *Handlers) AppendEventIdentity(w http.ResponseWriter, r *http.Request)
AppendEventIdentity is the identity-family ingest entrypoint — POST /v1/internal/identities/event. Same contract as the agent lanes (raw inner-event body + X-Signature detached JWS, 256 KiB cap); the identity codec enforces the IDENTITY_* enum and the identityId keyspace.
func (*Handlers) AppendEventV1 ¶
func (h *Handlers) AppendEventV1(w http.ResponseWriter, r *http.Request)
AppendEventV1 is the V1 ingest entrypoint.
func (*Handlers) AppendEventV2 ¶
func (h *Handlers) AppendEventV2(w http.ResponseWriter, r *http.Request)
AppendEventV2 is the V2 ingest entrypoint.
func (*Handlers) GetAgentIdentities ¶ added in v0.1.6
func (h *Handlers) GetAgentIdentities(w http.ResponseWriter, r *http.Request)
GetAgentIdentities handles GET /v1/agents/{agentId}/identities — the computed list of identities currently linked to the agent. Identical entries to the badge's identities[] field, served alone and paginated (TL limit/offset convention) — the overflow target for the badge's embedded cap.
func (*Handlers) GetAgentIdentityHistory ¶ added in v0.1.6
func (h *Handlers) GetAgentIdentityHistory(w http.ResponseWriter, r *http.Request)
GetAgentIdentityHistory handles GET /v1/agents/{agentId}/identities/history — the link/unlink events that ever named this agent, in the standard audit envelope (each record a TransparencyLog), filtered through the agent index. Past and present associations fall out of reading those events; current state is the computed identities[] join.
func (*Handlers) GetAudit ¶
func (h *Handlers) GetAudit(w http.ResponseWriter, r *http.Request)
GetAudit handles GET /v1/agents/{agentId}/audit. Matches the reference TransparencyLogAudit shape — { records: [TransparencyLog, ...] }.
func (*Handlers) GetBadge ¶
func (h *Handlers) GetBadge(w http.ResponseWriter, r *http.Request)
GetBadge handles GET /v1/agents/{agentId}. Returns the reference-shaped TransparencyLog JSON: merkleProof, payload (the V1 envelope's payload piece), schemaVersion, signature (TL attestation), status — plus the computed identities[] join (the agent's currently-linked verified identities, decorated with each identity's current stream state). The join is read-time: rotation and revocation on the identity stream are visible here immediately with zero agent-stream writes.
func (*Handlers) GetCheckpointHistory ¶
func (h *Handlers) GetCheckpointHistory(w http.ResponseWriter, r *http.Request)
GetCheckpointHistory handles GET /v1/log/checkpoint/history. Accepts reference filter params (limit, offset, fromSize, toSize, since, order); returns CheckpointHistoryResponse shape (§1335).
func (*Handlers) GetCheckpointJSON ¶
func (h *Handlers) GetCheckpointJSON(w http.ResponseWriter, r *http.Request)
GetCheckpointJSON handles GET /v1/log/checkpoint — the JSON-encoded view of the current checkpoint.
The raw sumdb-note bytes are served at /checkpoint via the file-server mount (matches C2SP tlog-tiles conventions + the reference TL's root-level `/checkpoint` path). This endpoint is the reference's `/v1/log/checkpoint` JSON shape byte-for-byte.
func (*Handlers) GetIdentityAgents ¶ added in v0.1.6
func (h *Handlers) GetIdentityAgents(w http.ResponseWriter, r *http.Request)
GetIdentityAgents handles GET /v1/identities/{identityId}/agents — the reverse join: the agents this identity currently links to, each decorated with its own computed badge status so a reader checks both ends of the link in one response.
func (*Handlers) GetIdentityAudit ¶ added in v0.1.6
func (h *Handlers) GetIdentityAudit(w http.ResponseWriter, r *http.Request)
GetIdentityAudit handles GET /v1/identities/{identityId}/audit — the identity's full event chain in the same audit envelope as the agent audit ({ records: [TransparencyLog, ...] }).
func (*Handlers) GetIdentityBadge ¶ added in v0.1.6
func (h *Handlers) GetIdentityBadge(w http.ResponseWriter, r *http.Request)
GetIdentityBadge handles GET /v1/identities/{identityId} — the identity badge: latest sealed identity event + inclusion proof + computed status (VERIFIED | REVOKED).
func (*Handlers) GetIdentityReceipt ¶ added in v0.1.6
func (h *Handlers) GetIdentityReceipt(w http.ResponseWriter, r *http.Request)
GetIdentityReceipt handles GET /v1/identities/{identityId}/receipt — a SCITT COSE_Sign1 receipt for the identity's latest sealed event. Same machinery, content type, and 503 retry semantics as the agent receipt.
func (*Handlers) GetReceipt ¶
func (h *Handlers) GetReceipt(w http.ResponseWriter, r *http.Request)
GetReceipt handles GET /v1/agents/{agentId}/receipt.
Returns a SCITT COSE_Sign1 receipt as binary CBOR bytes with Content-Type `application/scitt-receipt+cose`. The body is not JSON — it is the CBOR-encoded COSE_Sign1 tagged structure (tag 18) containing the inclusion proof and ES256 signature over the event's canonical bytes.
When the event has been appended but no checkpoint yet covers it, the handler returns 503 Service Unavailable with a Retry-After header. This matches RFC 7231 guidance and gives clients a cheap retry loop.
func (*Handlers) GetRootKeys ¶
func (h *Handlers) GetRootKeys(w http.ResponseWriter, _ *http.Request)
GetRootKeys handles GET /root-keys.
Returns the active TL verifier public keys in sumdb-note verification-line format — one line per key:
<origin>+<keyhash-hex>+<base64(0x02 || SPKI-DER)>\n
served as text/plain. Matches the reference TL's `GetRootVerificationKeys` controller shape. Verifiers call this once at session start and cache the result; rotation is expressed by adding a new line while the old one is still trusted, not by mutating the response. The 4-byte keyhash embedded in each line gives verifiers an O(1) `kid`-to-key lookup against COSE receipts and checkpoint signatures. (See cmd/ans-tl/main.go where the body is pre-rendered at startup.)
func (*Handlers) GetSchema ¶
func (h *Handlers) GetSchema(w http.ResponseWriter, r *http.Request)
GetSchema handles GET /v1/log/schema/{version}.
Returns the embedded JSON schema describing the V{n} envelope shape, byte-for-byte, with Content-Type: application/json. 404 if the version string isn't one we bundle.
func (*Handlers) GetStatusToken ¶
func (h *Handlers) GetStatusToken(w http.ResponseWriter, r *http.Request)
GetStatusToken handles GET /v1/agents/{agentId}/status-token.
Returns a signed COSE_Sign1 status token (CBOR tag 18) carrying the agent's current lifecycle state plus enough identity data (cert fingerprints, metadata hashes) for verifiers to check a connection without hitting the badge endpoint. Behaves like OCSP stapling for agents.
Response codes mirror reference swagger §283-308:
200 OK — binary CBOR, application/ans-status-token+cbor 404 Not Found — no events exist for the agent 410 Gone — agent in a terminal state (EXPIRED / REVOKED) 501 Not Implemented — status tokens disabled on this instance
A "disabled" deployment is one where the operator did not wire a StatusTokenService (e.g., they don't want to hold a third signing key). 501 tells clients to stop trying and fall back to the badge.
func (*Handlers) Mount ¶
Mount registers every route on r. The static tile/checkpoint file server is mounted at /checkpoint + /tile/* and points at Tessera's POSIX data directory so the files Tessera writes are served as-is.
/root-keys is registered before the tile catch-all so chi routes the specific path to our handler instead of attempting to serve a (non-existent) `root-keys` file from the tile root.