Name
groundhog-http: the version 1 log API over a Unix domain socket.
Groundhog stores and serves the durable event log.
Applications build derived views from replay or follow.
Transport
The service speaks HTTP/1.1 over the Unix socket configured by[server].socket.
It does not listen on TCP.
HTTP/1.1 requires a Host header, but Groundhog ignores its value.
POST routes accept Content-Type: application/json or no content type.
Groundhog accepts charset=utf-8 as the only optional parameter.
Other parameters or media types return 415 before Groundhog reads the body.
Any Content-Encoding header also returns 415 before body reading.
Authentication
If[server].token is not empty, every request needs:
curl:
Current routes
An unknown path returns 404.
A known path with another method returns 405 and an
Allow header.
HEAD returns the same status and headers as GET without a body.
POST /v1/query and GET /v1/catalog are not routes in Groundhog 0.2.
They return 404.
Error documents
Errors use a stable code and a separate message:error codes for control flow.
They must not match message text because that text can change.
Clients must retain or ignore unknown top-level fields.
An unknown error code remains an error with the received HTTP status.
Some conflicts add typed top-level fields.
Per-event validation adds an errors array with zero-based indexes, stable codes, and messages.
Groundhog rejects repeated query parameters, duplicate JSON member names, and unknown body members.
Control envelopes have a 1 MiB limit.
JSON ingest has a separate 32 MiB limit.
POST /v1/events
Append one atomic batch:
v defaults to 1.
source and batch_id belong to the batch.
This route does not accept query parameters.
Each submitted event has stream, record_key, kind, optional occurred_at, and payload.
Groundhog rejects the complete batch when any event is invalid.
A batch contains 1 through 10,000 events and no more than 32 MiB of request-body JSON.
Source, stream, and kind values use at most 128 UTF-8 bytes.
Record keys use at most 1,024 UTF-8 bytes, and batch IDs use at most 256 UTF-8 bytes.
Source and stream names match [a-z0-9_][a-z0-9_.-]*.
Groundhog reserves the system source and the groundhog/ batch ID prefix.
Groundhog canonicalizes accepted payloads with RFC 8785 rules.
It rejects non-finite numbers and precision-losing integer aliases.
It also rejects payloads deeper than 128 containers.
An occurred_at value must be a valid UTC calendar time with a final Z.
It can omit fractional seconds or use one through nine fractional digits.
Numeric offsets and leap seconds are invalid.
Idempotency
(source, batch_id) identifies one batch for the life of the data directory.
Groundhog computes a canonical digest before it assigns server fields.
The digest binds the source and ordered submitted event content.
It excludes batch_id, stream_precondition, and server-assigned fields.
A new batch returns:
status: "duplicate".
The retry writes nothing after lifecycle state or the stream frontier changes.
Different digested content with the same key returns 409 batch_id_conflict and writes nothing.
A successful receipt means the complete batch is durable.
Retry the same submitted events with the same key after a lost response.
Optional stream precondition
stream_precondition requires one stream to have an expected frontier before the batch commits.
Every event in the batch must target that stream.
expected_frontier to null to require a stream with no committed events.
A mismatch returns 409 stream_frontier_conflict and commits nothing.
GET /v1/events
Finite replay returns authoritative history in increasing event_id order:
after is exclusive.
limit must be positive and cannot exceed [replay].max_limit.
Omission uses [replay].default_limit.
A finite response has this shape:
last_event_id is the last matching event and is absent for an empty result.
It is not the progress cursor for a filtered consumer.
next_after is the last position that the request conclusively scanned.
Persist it for the next finite poll.
A finite page can contain fewer events than limit when it reaches the response-size bound.
snapshot_through_event_id is the coherent frontier captured for the request.
It is null only for an empty log.
Follow mode
Addfollow=true to receive the initial snapshot and later commits on one response.
Content-Type: application/x-ndjson and returns one JSON record per line.
The response includes Connection: close.
The records have three forms:
eventsrecords contain replay fields and aphaseofsnapshotorlive.- A
caught_uprecord marks the end of the initial snapshot. - An
endrecord gives a terminal reason and the last delivered event ID.
shutdown, writer_poisoned, session_closed, buffer_exceeded, and replay_failed.
An end record can include a descriptive detail field.
EOF without an end record is a transport failure.
In follow mode, limit bounds each events record.
It does not end the response.
Persist each delivered event’s event_id.
Reconnect with after=<last delivered event_id> and the same filters.
follow=false or an omitted follow returns one finite JSON response.
GET /v1/streams
This route scans the durable log and returns source and stream summaries.
It does not use derived state.
source, after, through, and limit.
Results use source order followed by stream order.
The after value is an exclusive source/stream cursor.
next_after is not null, send it as after on the next request.
Also send the first response’s snapshot_through_event_id as through.
Groundhog rejects after without through.
The anchor excludes later commits, so all pages describe one logical stream snapshot.
POST /v1/sources/retire
Permanently close one source to new batches:
v defaults to 1.
The source must have at least one committed event.
Operators cannot retire the reserved system source.
A first success returns:
status: "already_retired".
A new batch for the retired source returns 409 source_retired.
Retirement does not rename, delete, seal, or compact existing history.
It appends one event under source system and stream groundhog.source_lifecycle.
The event uses kind source_retired and the retired source as its record key.
A successor can declare one retired predecessor in its first batch.
The first submitted event uses stream groundhog.source_lineage and kind source_succeeded.
Its record key equals the predecessor source, and it omits occurred_at.
Its closed payload contains v, predecessor_source, and predecessor_final_frontier.
The declared frontier must equal the predecessor’s durable retired frontier.
Groundhog returns 400 invalid_source_lineage for a malformed or misplaced marker. It returns
409 source_lineage_conflict when the predecessor retirement or successor state conflicts.
Common status codes
A 429 rejected before mutation queue admission writes nothing and is retryable.
Queued mutations reach their real storage result after a client disconnects.
A 503 means the writer cannot accept more mutations in this serving session.
Restart the service before retrying.
Unavailable forms
Groundhog 0.2 does not implement local SQL,/v1/query, or /v1/catalog.
It also does not implement NDJSON ingest, POST /v1/imports, observation ingest, or a TCP listener.
Sending these routes or media types does not activate partial behavior.
See also
serve,
configuration,
events,
getting started