IDKMesh

API v1 beta: compatibility, deprecation and migration

This guide is for anyone calling the Control Tower local API (/api/v1/...) or using the Python client (idkmesh.api_client.ControlTowerClient). It states what is stable, how change is announced, and which behaviour changed while the API was being qualified, so you can check your integration against it.

The API is a loopback, single-token, read/inspect profile. It is not a multi-user service, and nothing here describes network exposure. Whether a given revision is releasable as “v1 beta” is decided by the beta release gate; until that gate reports ready, treat every behaviour below as pre-beta.

What you can rely on

Frozen by API Conventions v0.1; a change to any of these is a breaking change that needs an explicit decision record:

URL version and object schema version are independent. A new schema_version inside /api/v1 is not a new API version.

What is not stable yet

How change is announced

Class What it means for you Where it appears
Documentation only none CHANGELOG
Additive compatible new fields, endpoints or codes — ignore what you do not know CHANGELOG, Added
Behaviour clarification a previously unspecified case now behaves one defined way CHANGELOG, Fixed
Deprecated still works; has a replacement and an earliest-removal version CHANGELOG, Deprecated
Breaking needs migration; has a decision record and migration notes CHANGELOG and release notes

A deprecation states the affected version/path/object, the replacement, the earliest removal date or version, and a migration guide (section 20 of the conventions). Nothing is removed merely because a newer UI stopped using it.

No deprecation is currently in force. The CHANGELOG has no Deprecated entries; if that changes, this guide is updated in the same change.

Behaviour changes to check

These were clarifications found while qualifying the API (issue #745). Each is covered by a test; none changed a documented contract, but each changes what an unusual request returns. Check the ones that apply to you.

Request Before Now Test
Deeply nested JSON body or report raw RecursionError escaped controlled error (ProtocolError in the client, invalid_event_json for audit events) tests/test_api_qualification_fuzz.py
Accept with an explicit q=0 served anyway 406 not_acceptable if all entries are q=0; q=0 on the vendor type falls back to application/json tests/test_api_header_boundary_fuzz.py
Response Content-Type differing only in case or whitespace client raised ProtocolError accepted, normalised like the server tests/test_api_header_boundary_fuzz.py
Content-Length such as +2 or -0 accepted 400 invalid_content_length (digits only, RFC 9110) tests/test_api_header_boundary_fuzz.py
X-IDKMesh-UI-Token with a non-ASCII byte connection dropped, no response 403 invalid_session_token tests/test_api_security_matrix.py
Unauthenticated request with a stray query string 400 unexpected_query_parameters 403 invalid_session_token (the query is judged only after the token) tests/test_api_security_matrix.py
PUT/PATCH/DELETE/TRACE on /readyz 404 not_found 405 method_not_allowed, Allow: GET, HEAD tests/test_api_security_matrix.py
Reading an event whose stored payload no longer matches its digest altered content served 500 store_error; the altered content is withheld tests/test_api_storage_integrity.py
ProductSpineRun.transition() given identity or authority fields (library) silently applied invalid_update tests/test_api_state_idempotency_properties.py

Making your integration robust

Checking a revision

make qualify-api BASE=<previous-tag> STRICT=1   # contract, fuzz, security, reliability, install
make beta-readiness                              # is the release gate satisfied?
python tools/schema_compat_check.py --base <previous-tag>

make qualify-api writes a JSON report with the exact commands and results. See TESTING.md.

Reporting a problem

Report a compatibility regression as an issue with the request, the response, and the revision. Report a vulnerability through SECURITY.md, never a public issue.