9 min read
Core Concepts
StateAnchor is built on a small number of precise concepts. Understanding them makes the entire system predictable. This page defines each concept, explains why it exists, and gives a concrete example.
Desired-state control plane
StateAnchor is a desired-state control plane for APIs. You declare what your API should be in stateanchor.yaml. StateAnchor continuously reconciles reality against that declaration -- detecting when code, runtime, or generated artifacts diverge from it.
The spec is the single source of truth. Everything else -- SDKs, MCP servers, docs, OpenAPI specs -- is derived from it and mechanically tied to the commit that produced it. Nothing writes back to the spec automatically. Only human git commits modify it.
This is the same pattern Kubernetes uses for infrastructure. You declare desired state, controllers reconcile toward it, and status reports the difference. StateAnchor applies this to APIs.
| Kubernetes | StateAnchor |
|---|---|
| Manifest (YAML) | stateanchor.yaml |
| Desired state | Spec endpoints, models, auth |
| Observed state | Previous IR + deployed API |
| Controller | Sync pipeline |
| Admission Controller | Gate engine (ERR blocks, WARN thresholds) |
| Status | Sync run: gate_action, lane, findings, artifacts |
| Reconciliation | Diff → gate → generate → publish |
Example: You have a payments API with four endpoints. You add stateanchor.yaml declaring those endpoints, their auth scheme, and their response models. On every git push, StateAnchor reads this file, diffs it against the previous version, scores the changes, and regenerates all derived artifacts. If you remove an endpoint, the gate blocks the push. If you add one, it passes and regenerates SDKs automatically.
The three planes
StateAnchor separates concerns into three planes that must never be conflated. If a feature requires the observation plane to overwrite the desired-state plane, it is rejected -- that path creates thrash, false authority, and spec corruption.
Desired-state plane
stateanchor.yaml in your repo root. This is the only authoritative source. Nothing writes back to this plane automatically. The spec is never "corrected" to match runtime behavior -- runtime observations surface warnings, they do not write truth.
Observation plane
Source scanners, runtime probes, deployed-spec comparison. Observational only. Never authoritative. The observation plane tells you what is -- the desired-state plane tells you what should be. When they disagree, the desired-state plane wins. The live API scanner is an observation plane tool: it reports divergence as an operational signal, not as a correction.
Derivation plane
SDKs, MCP servers, docs, OpenAPI specs. Derived from the desired-state plane only. Never become source of truth. If a derived artifact diverges, the system regenerates -- it never modifies the spec to match the artifact. Every artifact carries a provenance header tying it to the exact spec commit and IR hash that produced it.
Intermediate Representation (IR)
The IR is a canonical, structured JSON model of your API derived from stateanchor.yaml. It normalizes endpoints, models, auth, and metadata into a form that generators consume. The IR is the stable internal boundary -- generators never read the raw YAML directly.
The IR exists because raw YAML is not diffable in a semantically meaningful way. Two YAML files can have different whitespace, key ordering, and comment structure but represent the same API. The IR normalizes these away. Diffs are computed between consecutive IRs, not between raw YAML files.
The IR is stored on every completed sync run. This creates a timeline of your API's structural evolution that you can query, diff, and audit.
IR shape (simplified):
{
"service": "payments-api",
"version": "2.4.0",
"base_url": "https://api.acme.com/v2",
"auth": { "type": "bearer" },
"endpoints": [
{
"id": "createCharge",
"method": "POST",
"path": "/charges",
"parameters": [],
"request_body": "ChargeInput",
"responses": { "201": "Charge" },
"auth_required": true
}
],
"models": {
"Charge": {
"fields": [
{ "name": "id", "type": "uuid", "required": true },
{ "name": "amount", "type": "integer", "required": true },
{ "name": "currency", "type": "string", "required": true }
]
}
}
}When you push a change that renames amount to amount_cents, the diff engine sees a field_renamed operation on the Charge model. It does not see a YAML text diff -- it sees a semantic API change.
Drift
Drift is a formal, measurable deviation between desired state and observed or derived state. It is not a heuristic or approximation. In StateAnchor, drift is a SHA mismatch: the content hash of the current spec file on GitHub differs from the content hash at the time of the last completed sync. If they match, there is no drift. If they differ, the diff engine classifies every change.
There are two distinct types of drift:
Spec drift: the stateanchor.yaml file has changed since the last sync. Detected by comparing the Git blob SHA of the file. This triggers a new sync run that diffs the IR, evaluates the gate, and regenerates artifacts.
Runtime drift: the deployed API has diverged from the spec. Detected by the live API scanner, which fetches the OpenAPI document from the running service and compares it against the canonical IR. Runtime drift does not block syncs -- it surfaces as an advisory finding on the project dashboard.
Example: A developer hotfixes a production API to add a temporary field. The change works, gets forgotten, and is never backported to stateanchor.yaml. The next time the scanner runs, it detects the divergence and flags it as runtime drift. The project shows "Drift detected" in the dashboard until the spec is updated or the hotfix is reverted.
Drift classification
| Change kind | Lane | Score | Description |
|---|---|---|---|
endpoint_removed | ERR | 40 | An existing endpoint was deleted |
auth_changed | ERR | 35 | Authentication scheme changed |
field_removed | ERR | 30 | A field was removed from a model |
type_changed | ERR | 25 | Field type changed (e.g., string → number) |
required_added | WARN | 20 | A new required parameter was added |
field_renamed | WARN | 15 | A field was renamed |
endpoint_added | INFO | 0 | A new endpoint was added |
field_added_optional | INFO | 0 | A new optional field was added |
description_changed | INFO | 0 | Only description text changed |
Gate engine
The gate engine evaluates every spec change and produces a binary decision: block or proceed. It classifies each change into a lane (ERR, WARN, INFO) and applies the highest-severity lane present.
ERR lane changes always block. There is no threshold that allows them through. The only bypass is a drift exception with a named approver and a maximum 90-day TTL.
WARN lane changes block if the count of warnings meets or exceeds the configured threshold (default: 1). This means a single WARN finding blocks by default. Set warn_count_threshold: 0 to allow WARN items to pass without blocking.
INFO lane changes always pass. They are logged in the sync run findings but never affect the gate decision.
For the complete gate engine reference, see the Gate Engine page.
Artifacts and provenance
Artifacts are the generated outputs of a sync run: TypeScript SDKs, Python SDKs, Go SDKs, MCP servers, and documentation. Each artifact is stored in content-addressed storage keyed by SHA-256. If two different commits produce identical output, they resolve to the same storage object.
Every artifact carries a provenance record embedded as a comment header:
// @stateanchor-provenance: <base64-encoded JSON>The provenance contains:
source_ir_hash-- SHA-256 of the IR that produced itgenerator_version-- version of the generation enginespec_sha-- commit SHA that triggered the generationartifact_type-- wrapper, mcp, or docsproject_id-- the project UUIDgenerated_at-- ISO 8601 timestamp
This means you can answer "what commit produced this SDK version?" for any artifact, at any point in time, without relying on git history or release tags. The provenance is in the artifact itself.
Sync run
A sync run is the atomic unit of work in StateAnchor. Every git push (or manual trigger) creates a sync run that progresses through six stages:
- Stage A -- Spec Validation. The raw YAML is parsed and validated. Malformed specs fail here immediately.
- Stage B -- IR Generation. The validated spec is converted to the canonical IR. The IR is stored and hashed.
- Stage B½ -- Spec Diff + Gate. The new IR is diffed against the previous IR. Each change is classified into a lane. The gate evaluates the findings and produces a block or proceed decision. If blocked, the run stops here -- no code is generated.
- Stage C -- Code Generation. For each configured output language, Claude generates the SDK or MCP server from the IR. Generation runs in parallel across languages.
- Stage D -- Output Validation. Each generated artifact is structurally validated (type-checked for TypeScript, syntax-checked for Python). Validation failures trigger a single retry with error feedback.
- Stage E -- Storage + Provenance. Validated artifacts are stored in content-addressed storage with provenance metadata. Project refs are updated to point to the new artifacts.
Every stage logs timing, status, and error details to the sync run record. The complete audit trail is queryable per project and per run.
Rollback
Rollback in StateAnchor is a pointer move, not a regeneration. Every sync run stores the complete IR and artifact references. Rolling back means pointing the current environment reference to a previous sync run's artifacts. The artifacts already exist in content-addressed storage -- no regeneration needed. This makes rollback instant and deterministic.
Drift velocity
Drift velocity is the rate at which a project's spec changes produce breaking diffs. StateAnchor computes this by fitting a linear regression over the breaking scores of recent sync runs. When the projected score crosses a configurable threshold within the projection horizon (default: 3 commits), a PREDICTIVE_WARN advisory fires. This gives teams early warning that their current rate of API changes will hit a hard block soon.