# Client API Stability The contract between EveryCycle and everything that talks to it. Compatibility shims, third-party clients, distro defaults, and operator tooling all depend on this commitment being honored. This document is the policy. The crate that defines the surface is `everycycle-api`. ## The freeze gate The API is **explicitly unstable** until the v1 freeze. The freeze trips when **both** are true: 1. Thread A has reached A4 (cross-vendor activation handoff exercised in practice; the trait shape has survived contact with a second vendor). 2. A second external client has shipped against the API and is in production use. Whichever condition lands last trips the gate. A date does not trip the gate. Until the gate trips: - The crate constant `API_UNSTABLE` is `true` and `API_VERSION` is `"0"`. - Any release may break the wire format, the type shapes, the IPC framing, or the socket location. - Clients should pin to an exact daemon version and refuse to connect to a daemon whose advertised version differs. - Breakage is announced in the release notes for the breaking release. There is no migration window pre-freeze. When the gate trips: - `API_VERSION` becomes `"1"`, `API_UNSTABLE` becomes `false`. - The rules in **What "stable" means** below apply. The gate is one-way. After the freeze trips, the API cannot be re-marked unstable; it can only progress to v2 under the rules below. ## What is in scope The stability commitment covers: - The public types in `everycycle-api` (`ClientRequest`, `ServerEvent`, `ModelRef`, `PromptSpec`, `SamplingParams`, `Priority`, `TokenChunk`, `FleetSnapshot`, `ErrorKind`, and everything they transitively contain). - The wire format: CBOR over length-prefixed frames as documented in `transport.rs`. - The IPC framing constants (`FRAME_LENGTH_BYTES`, `MAX_FRAME_BYTES`). - The default socket path (`/run/everycycle/api.sock`) and the `EVERYCYCLE_SOCKET` environment-variable override. - The semantics of `RequestKind` variants and the event-stream lifecycle (every request terminates either with an event whose `finished` discriminator is set, or with `ServerEvent::Error`). ## What is out of scope The stability commitment does **not** cover: - Internal daemon types, including the `Executor` trait in `everycycle-runtime`. - Telemetry schemas (Prometheus metric names, OpenTelemetry attribute keys). These have their own stability story, set per the operator-surface charter when it lands. - The HAL inventory schema (`everycycle-hal::inventory::DeviceInventory`). It is consumed by appraise and the TUI but is not part of the client surface; revising it is internal. - The probe/appraise schema (`everycycle-hal::capability::*`). Appraisal reports have their own format-version field independent of the API version. - Diagnostics in error messages. The `ErrorKind` enum is stable; the `message: String` carried with an error is not. - Performance characteristics. Throughput, latency, queueing behavior, and admission policy are tuning concerns, not API concerns. ## What "stable" means after the freeze Once `API_VERSION` is `"1"`: **Patch releases** (`1.x.Y`) are bug-fix only. No new variants, no new fields, no behavior changes that a correctly written client would notice. **Minor releases** (`1.X.0`) are strictly additive: - New `RequestKind` variants may be added. Existing clients ignore unknown variants in their own events stream by definition (they never sent that request). - New `ServerEvent` variants may be added; clients must tolerate unknown event variants by skipping them, and the daemon will not send an unknown-shaped event to a client that hasn't first sent a request of a kind that can produce it. - New optional fields may be added to existing types, default-valued for serde compatibility. Required fields may not be added. - New `ErrorKind` variants may be added; clients must treat unknown error kinds as `ErrorKind::Internal` for handling purposes. - The framing constants and socket path are stable; minor releases do not move them. **Major releases** (`X.0.0`) are reserved for changes that cannot be expressed under the additive rules above. A major release requires: - A migration document published with the release notes, with side-by-side before/after for every breaking change. - A deprecation window of **at least one full release cycle on every supported distro** before the deprecated surface is removed. "Release cycle" means the cadence of the underlying distro (e.g. Fedora's release cadence), not the daemon's own. - The previous major version's daemon binary remains buildable from source for the duration of the deprecation window, so operators can run both side-by-side during migration. - A bump in `API_VERSION` to the new major (`"2"`, `"3"`, ...). The deprecation window is the cost the project pays for breaking the contract. The pricing is intentional. Asking shim authors, distro packagers, and third-party clients to chase a moving API is the failure mode this policy exists to prevent. ## Versioning of `everycycle-api` the crate The crate version on crates.io follows the API version. `everycycle-api 1.x.y` corresponds to `API_VERSION = "1"`. A pre-freeze `0.x` is permitted but not committed to — the gate decides. ## Versioning of the daemon The daemon (`everycycled`) has its own semver track that is **independent** of the API version. A given daemon major version may speak multiple API major versions if backward compatibility is feasible; conversely, a daemon major version bump does not imply an API change. The daemon's advertised API version is what clients negotiate against, not its own version. ## Negotiation Connection handshake (added when the daemon's listening code lands; described here so the API surface is consistent with it): 1. Client connects to the socket. 2. Daemon sends a single frame containing its `API_VERSION` string and a list of supported `API_VERSION` strings (for the case where it speaks more than one). 3. Client either proceeds (its expected version is in the list) or disconnects. Pre-freeze clients should additionally check `API_UNSTABLE` and refuse to operate against a daemon built from a different commit than they were. ## Out-of-band breaking changes Security fixes that require a wire-format change are the only carve-out. They are still announced with a migration document but may ship faster than the standard deprecation window allows. Any use of this carve-out is logged in the project's security-disclosure record (which does not exist yet — see `CONTRIBUTING.md`'s "What this file does not cover yet").