Skip to main content

max / everycycle

6.5 KB · 99 lines History Blame Raw
1 # Client API Stability
2
3 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.
4
5 This document is the policy. The crate that defines the surface is `everycycle-api`.
6
7 ## The freeze gate
8
9 The API is **explicitly unstable** until the v1 freeze.
10
11 The freeze trips when **both** are true:
12
13 1. Thread A has reached A4 (cross-vendor activation handoff exercised in practice; the trait shape has survived contact with a second vendor).
14 2. A second external client has shipped against the API and is in production use.
15
16 Whichever condition lands last trips the gate. A date does not trip the gate.
17
18 Until the gate trips:
19
20 - The crate constant `API_UNSTABLE` is `true` and `API_VERSION` is `"0"`.
21 - Any release may break the wire format, the type shapes, the IPC framing, or the socket location.
22 - Clients should pin to an exact daemon version and refuse to connect to a daemon whose advertised version differs.
23 - Breakage is announced in the release notes for the breaking release. There is no migration window pre-freeze.
24
25 When the gate trips:
26
27 - `API_VERSION` becomes `"1"`, `API_UNSTABLE` becomes `false`.
28 - The rules in **What "stable" means** below apply.
29
30 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.
31
32 ## What is in scope
33
34 The stability commitment covers:
35
36 - The public types in `everycycle-api` (`ClientRequest`, `ServerEvent`, `ModelRef`, `PromptSpec`, `SamplingParams`, `Priority`, `TokenChunk`, `FleetSnapshot`, `ErrorKind`, and everything they transitively contain).
37 - The wire format: CBOR over length-prefixed frames as documented in `transport.rs`.
38 - The IPC framing constants (`FRAME_LENGTH_BYTES`, `MAX_FRAME_BYTES`).
39 - The default socket path (`/run/everycycle/api.sock`) and the `EVERYCYCLE_SOCKET` environment-variable override.
40 - 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`).
41
42 ## What is out of scope
43
44 The stability commitment does **not** cover:
45
46 - Internal daemon types, including the `Executor` trait in `everycycle-runtime`.
47 - Telemetry schemas (Prometheus metric names, OpenTelemetry attribute keys). These have their own stability story, set per the operator-surface charter when it lands.
48 - 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.
49 - The probe/appraise schema (`everycycle-hal::capability::*`). Appraisal reports have their own format-version field independent of the API version.
50 - Diagnostics in error messages. The `ErrorKind` enum is stable; the `message: String` carried with an error is not.
51 - Performance characteristics. Throughput, latency, queueing behavior, and admission policy are tuning concerns, not API concerns.
52
53 ## What "stable" means after the freeze
54
55 Once `API_VERSION` is `"1"`:
56
57 **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.
58
59 **Minor releases** (`1.X.0`) are strictly additive:
60
61 - New `RequestKind` variants may be added. Existing clients ignore unknown variants in their own events stream by definition (they never sent that request).
62 - 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.
63 - New optional fields may be added to existing types, default-valued for serde compatibility. Required fields may not be added.
64 - New `ErrorKind` variants may be added; clients must treat unknown error kinds as `ErrorKind::Internal` for handling purposes.
65 - The framing constants and socket path are stable; minor releases do not move them.
66
67 **Major releases** (`X.0.0`) are reserved for changes that cannot be expressed under the additive rules above. A major release requires:
68
69 - A migration document published with the release notes, with side-by-side before/after for every breaking change.
70 - 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.
71 - 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.
72 - A bump in `API_VERSION` to the new major (`"2"`, `"3"`, ...).
73
74 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.
75
76 ## Versioning of `everycycle-api` the crate
77
78 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.
79
80 ## Versioning of the daemon
81
82 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.
83
84 The daemon's advertised API version is what clients negotiate against, not its own version.
85
86 ## Negotiation
87
88 Connection handshake (added when the daemon's listening code lands; described here so the API surface is consistent with it):
89
90 1. Client connects to the socket.
91 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).
92 3. Client either proceeds (its expected version is in the list) or disconnects.
93
94 Pre-freeze clients should additionally check `API_UNSTABLE` and refuse to operate against a daemon built from a different commit than they were.
95
96 ## Out-of-band breaking changes
97
98 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").
99