Stability

What you can build on, what may change, and how a change is announced. hookinlet is pre-1.0: this page is the promise for v0.1.0 and after, and CHANGELOG.md is where every change that touches it is written down.

The HTTP API

The read API, the stream, the admin API and ingest are described by one document, openapi.json, which the server is tested against: every response it can give is in the document, and every response in the document is given by a test. Paths carry the major version (/v1/...). Ingest (/in/...), the health checks, /metrics and /mcp are not versioned in the path: ingest is what senders are configured with and does not change shape, the health checks and metrics are for platforms, and MCP is versioned by its own protocol and its tool names. Until 1.0 a breaking change is made in place, as a minor version with a CHANGELOG entry (this is how the admin API came to be under /v1/admin); from 1.0, a breaking change to /v1 would be /v2, served beside it.

Not breaking (clients must be ready for them):

Breaking (a CHANGELOG entry, and while the version is below 1.0, a minor version): removing or renaming a path, field, parameter, code, tool or argument; changing a type or what a value means; making an optional request field required; a status that meant success meaning failure.

What the server is strict about

Cursors

A cursor is the seq of a message in one epoch of an inbox. Following next links is always safe. Keep the epoch with the cursor if you keep it past a restore; a 409 epoch_changed is the server telling you to start again.

Inbox identifiers

Inboxes are named by their slug everywhere a client reads or acts: the read API, the stream, MCP and the CLI. The admin API also shows a numeric id, which is the order in which inboxes were created, is never reused within a database (a database restored from an older backup hands out the ids it had then again), and is what the admin list's after cursor counts. Do not build on it beyond that: it is an internal key, and an embedder's Store may hand out another kind. Tokens have a numeric id for the same reason (to revoke one, and to page through them). An ingest key is not an identifier but a capability: treat it like a password for senders.

The command line

hookinlet <command> --json prints the same shapes as the API and is stable in the same way. The text output (tables, sentences) is for people and may change. Exit codes: 0 success, 1 the operation failed, 2 usage or configuration. Flags and HOOKINLET_* variables are added freely; one is removed or changes meaning only with a CHANGELOG entry.

The data

The database moves forward only, by migrations that run at start. A copy is kept before a migration (see Deploying). A version that finds a database written by a newer one refuses it. A backup restores into the version that took it and later ones.

The Go packages

pkg/inbox (the interfaces and types), pkg/inboxtest (their conformance suites), pkg/auth/token, pkg/secretbox, pkg/store/sqlite, pkg/notify/local, pkg/verify, pkg/server, and docs (which embeds openapi.json as docs.OpenAPI, for an embedder to serve, validate against, or generate from; the document is the contract and is stable as described above) are public. Until 1.0 they can change in breaking ways, every change is in CHANGELOG.md, and after 1.0 semantic versioning applies. Anything under internal/ is not.

How the interfaces grow. inbox.Store, inbox.Hooks, inbox.SecretBox, inbox.Authenticator, inbox.Principal and inbox.Notifier do not gain methods once they are 1.0: a new capability is a new, small interface that an implementation may also satisfy, found by a type assertion (as Backupper and ScopedPrincipal are), with the server falling back to what it did before. A test fails when the methods of one of those interfaces, their names or their signatures, change, so the change cannot be an accident. A conformance suite for a new interface ships with it, and a behaviour that is in no suite is not part of the contract.

Options. server.Options follows one rule: a zero value means the documented default; a negative number turns off a bound or a schedule where that makes sense (and is an error where it does not); nothing is required but Store, and an address only to use Run. Embedding the handler in your own http.Server needs no address.

What is not promised

Log messages and their fields, the metrics' help texts, the exact wording of error messages, response header order, JSON key order, and timing.

Cross-origin requests, TLS, and the Host header

From docs/stability.md at v0.1.1. Markdown · View on GitHub