Skip to content

RUST-CI-003 - Wire the Rust /api/v1 OpenAPI conformance/drift check into CI

Summary

EMULEBB_WORKSPACE_ROOT\repos\emulebb-tooling\docs\products\emulebb-rust\api\REST-API-OPENAPI.yaml is the source of truth for the Rust-forward /api/v1 contract. Today nothing automatically verifies that the daemon's live responses match the document, so the spec and implementation can silently drift. This item wires a conformance/drift check into CI so the Rust contract stays honest.

Why This Matters

The embedded SPA WebUI and first-party tests drive emulebb-rust directly from the Rust-forward contract. If a response shape diverges from the spec, the owned UI breaks with no early signal. A drift gate converts the "remember to update the YAML" convention into an enforced invariant.

This is not an API-freeze item. Before an explicit Rust REST freeze decision, there is no external consumer to preserve: the daemon, embedded SPA WebUI, OpenAPI, validators, and tests may change together whenever a cleaner contract is useful. The gate exists to keep the current chosen contract honest.

Intended Shape

  • Validate live daemon responses against the Rust OpenAPI artifact in tooling docs.
  • Run it from the shared emulebb-build-tests suite against a locally launched daemon bound to X_LOCAL_IP; do not fork a parallel per-client suite.
  • Fail on a response that violates the schema, an implemented route missing from the spec, or a spec route missing from the Rust router.
  • Keep contract-version handling consistent with docs/active/API-V1-COMPATIBILITY.md.

Scope Constraints

  • Conformance only; do not broaden or weaken the contract inside this item. Separate feature/API changes may intentionally reshape the contract, but must update the daemon, embedded SPA WebUI, OpenAPI, validators, and tests together.
  • Adapter surfaces (/api/v2 qBit-compat, Torznab) are out of scope for this native-contract gate.
  • No new tracked PowerShell; harness in Python via the shared suite.
  • emulebb-mfc conformance is a separate frozen-contract gate.

Acceptance Criteria

  • [ ] A conformance check validates live Rust /api/v1 responses against the Rust OpenAPI artifact in tooling docs.
  • [ ] It runs in this repo's CI / the shared emulebb-build-tests suite, not a forked suite.
  • [ ] Drift fails the gate (schema violation, implemented-but-unspecified route, or specified-but-unimplemented route).
  • [ ] The Rust API notes point at this item.

Validation

  • Run the check against a locally launched daemon bound to X_LOCAL_IP; confirm it passes on a clean HEAD and fails on an injected schema/spec mismatch.

Notes

  • Local item: it records an internal CI gate rather than a product feature. Promote to a GitHub-tracked CI item if it needs public workflow visibility.

2026-07-18 Progress

Added the first shared-harness drift guard: repos\emulebb-build-tests\scripts\check-rust-openapi-routes.py compares the emulebb-rust router path/method inventory against the Rust OpenAPI artifact and fails on implemented-but-undocumented or documented-but-unimplemented route inventory drift. This covers the route inventory part of the item; live response schema conformance and CI wiring remain open.

2026-07-18 Progress - CI Route Inventory Gate

The static route inventory guard now runs in repos\emulebb-build-tests\.github\workflows\fast-harness-ci.yml after the shared harness checks out emulebb-rust and emulebb-tooling. This moves the implemented/spec route-inventory drift check into CI. Live response schema validation remains the next uncovered part of this item.

2026-07-18 Progress - Static Query Inventory Gate

Extended the shared-harness route drift guard so the same CI step also compares Rust middleware query-parameter allowlists in crates\emulebb-rest\src\route_metadata.rs against OpenAPI query parameter names. This covers static query-name drift for implemented/documented routes; live response schema validation remains open.

2026-07-18 Progress - Static Body Inventory Gate

Extended the same shared-harness metadata drift guard to compare Rust top-level JSON body field allowlists in crates\emulebb-rest\src\route_body_metadata.rs against OpenAPI request-body schema property names. The CI gate now covers static route, query, and body metadata drift; live response schema validation remains open.

2026-07-18 Progress - Response Schema Validator Primitive

Extracted the REST smoke script's OpenAPI response schema validation into the importable shared-harness helper emule_test_harness\rust_openapi_responses.py. The helper validates named response components and operation/status responses against the same OpenAPI artifact, including component response $ref resolution. This gives the live daemon conformance step a reusable validator, but the full live daemon response gate remains open.

2026-07-18 Progress - Operation Response Validation Wiring

Updated the shared REST completeness smoke path so live responses are validated against the OpenAPI response schema for the exact operation and HTTP status that returned, instead of only the named success envelope. The remaining gap is running that live path as a CI daemon conformance gate under the policy quickstart.

2026-07-18 Progress - Existing Daemon Conformance Command

Added the persisted Python command repos\emulebb-build-tests\scripts\check-rust-rest-openapi-responses.py for validating an already-running emulebb-rust daemon against the OpenAPI response schemas. It reuses the shared REST completeness runner and performs no ad-hoc launching, so CI/profile orchestration can own daemon startup separately.

2026-07-18 Progress - Contract Header Metadata Gate

Extended the static OpenAPI drift checker to fail any documented native operation response that omits X-Contract-Version. The shared CI guard now covers route, query, body, and response-header metadata drift before live daemon schema conformance runs.

2026-07-18 Progress - Soak Describe Conformance Command

The Rust-only persisted profile --describe output now includes a restOpenApiConformanceCommand for the already-running daemon. It uses the reported LAN REST base URL and API key, writes a retained JSON report under the workspace output root, and keeps launch/validation discoverability inside the policy-owned Python quickstart path. Full automated live CI orchestration remains open.

2026-07-18 Progress - Conformance Base URL Preflight

Hardened the running-daemon conformance command to reject --base-url values that include a path such as /api/v1, preventing accidental /api/v1/api/v1 probes and giving operators a clean preflight error instead of a transport traceback.

2026-07-18 Progress - Static Auth Contract Gate

Extended the static OpenAPI drift checker to fail if the native contract loses its top-level ApiKeyAuth requirement, the X-API-Key header security scheme, or per-operation 401 response documentation. This keeps the documented auth surface aligned with the Rust router's API-key middleware before live response conformance runs.

2026-07-18 Progress - Auth Override Guard

Tightened the same static auth gate so an operation-level OpenAPI security override must still include ApiKeyAuth. This prevents a single native route from silently bypassing the documented X-API-Key requirement while the global security scheme remains valid.

2026-07-18 Progress - Live 405 Allow Header Check

Extended the REST smoke harness so the representative native 405 METHOD_NOT_ALLOWED probe must carry the documented Allow header in addition to the canonical JSON error envelope and contract-version header.

2026-07-18 Progress - Static 405 Contract Gate

Extended the static OpenAPI drift checker to fail if any native operation loses its 405 response or stops referencing MethodNotAllowedResponse, and to fail if that shared response component drops the documented Allow header.

2026-07-18 Progress - Static Contract-Version Gate

Pinned the OpenAPI contract-version values as schema constants and extended the shared route drift checker so Rust CONTRACT_VERSION, OpenAPI info.version/info.x-contract-version, the documented contract-version header, the capability discovery payload, and harness constants must all agree. The same gate also rejects inline X-Contract-Version response-header definitions that bypass the shared OpenAPI header component.

2026-07-18 Progress - Static Error Response Gate

Extended the static OpenAPI drift checker so every native operation must document 400, 401, 404, and default through the shared ErrorResponse component, and that component must continue to publish the canonical ErrorEnvelope JSON schema. 405 stays on the dedicated MethodNotAllowedResponse gate because it also owns the Allow header.

2026-07-18 Progress - Static Path Parameter Gate

Extended the static OpenAPI drift checker so every path template placeholder must have a matching in: path parameter document, no extra path parameters are documented, and every documented path parameter is marked required: true. This catches route-template/documentation drift before live response conformance runs.

2026-07-18 Progress - Static Operation Metadata Gate

Extended the static OpenAPI drift checker so every native operation must carry a non-empty operationId and tag list, and operation IDs must remain unique. This keeps adapter/code-generator-facing metadata stable while the Rust-forward contract continues to evolve before freeze.

2026-07-18 Progress - Static Success Response Gate

Extended the static OpenAPI drift checker so every native operation must document exactly one 2xx response, that success response must reference a shared response component, and the referenced component must define a concrete media schema. This keeps success-envelope documentation generator-ready and prevents route-local inline success shapes from bypassing the shared contract.

2026-07-18 Progress - Static Request Body Metadata Gate

Extended the static OpenAPI drift checker so every documented native request body must explicitly state required: true or required: false, expose only application/json, and reference a shared schema component. This preserves the current optional-body route where intentional while keeping JSON request payloads generator-ready and aligned with the Rust validators.

2026-07-18 Progress - Static Component Reference Gate

Extended the static OpenAPI drift checker so every $ref must be a local #/components/... reference and must resolve to an existing component. This gives missing or external schema, response, parameter, and header refs a precise drift report instead of failing later during response or schema checks.

2026-07-18 Progress - Static Parameter Metadata Gate

Extended the static OpenAPI drift checker so every inline or component parameter must publish an explicit boolean required value, a valid in location, a non-empty name, and a schema object. The OpenAPI contract now marks the existing optional query filters as required: false instead of relying on implicit OpenAPI defaults, which keeps generated clients and adapter manifests honest without changing Rust runtime behavior.

2026-07-18 Progress - Static Tag Taxonomy Gate

Extended the static OpenAPI drift checker so operation tags must be declared in the top-level tag taxonomy, top-level tags must be unique and named, and unused declared tags fail the route contract check. This keeps generated client groupings and power-user documentation navigation aligned with the Rust-native controller surface.

2026-07-18 Progress - Static Response Component Gate

Extended the static OpenAPI drift checker so every shared response component must have a non-empty description, must carry the shared contract-version header reference, and must expose the expected media type and schema. The gate preserves the intentional EventStreamResponse text/event-stream exception while keeping all other reusable response components JSON-envelope based.

2026-07-18 Progress - Static Operation Summary Gate

Extended the static OpenAPI operation metadata checker so every native operation must publish a non-empty summary alongside its stable operationId and tag list. This keeps generated client documentation and power-user route catalogs useful as the Rust-forward route surface evolves.

2026-07-18 Progress - Static Schema Component Gate

Extended the static OpenAPI drift checker so every shared schema component must be an object, must not be empty, and must declare a concrete type, composition, enum, or const shape. Empty enum definitions are rejected as well. This keeps the schema catalog generator-ready before adapters or power-user clients depend on it.

2026-07-18 Progress - Static Non-Empty Update Schema Gate

Extended the static OpenAPI schema-component checker so reusable *Patch and *Update DTOs must reject empty objects through minProperties: 1 or an equivalent required-field composition. This keeps sparse settings updates and other power-user PATCH bodies machine-readable instead of relying on prose or handler-side accidents.

2026-07-18 Progress - Static Documented Error Response Gate

Extended the static OpenAPI drift checker so every documented non-success, non-405 response must reference the shared ErrorResponse component, not just the required 400/401/404/default statuses. This keeps optional 409, 500, and 503 branches on the same canonical REST error envelope as the mandatory error surface.

2026-07-18 Progress - Static Closed Request Schema Gate

Extended the static OpenAPI request-body metadata checker so every documented JSON request body must reference a schema component with type: object and additionalProperties: false. This aligns the generated contract with the Rust body-field validators that reject non-object bodies and unknown JSON fields.

2026-07-18 Progress - Static SSE Response Header Gate

Extended the static OpenAPI response-component checker so the shared EventStreamResponse component must document the adapter-visible Cache-Control and X-Accel-Buffering headers in addition to the contract-version header. This keeps the live-events reverse-proxy contract locked in the same drift gate as the JSON response components.

2026-07-18 Progress - Static Parameter Reference Gate

Extended the static OpenAPI drift checker so every path-level and operation-level parameter must reference a shared #/components/parameters/... component. The remaining inline parameters, including Last-Event-ID and the transfer list filters, were moved into reusable components so adapter manifests and generated clients get one canonical parameter definition.

2026-07-18 Progress - Static Destructive Confirmation Gate

Extended the static OpenAPI drift checker so the shared destructive confirm query parameter must be required and every confirm-prefixed request-body field must be required with a boolean enum: [true] schema. This keeps shutdown, diagnostic dump/crash, log clearing, completed-transfer clearing, and shared-root replacement confirmations machine-readable and aligned with Rust validators.

2026-07-18 Progress - Static SSE Event Variant Gate

Extended the static OpenAPI drift checker so TransferEvent must remain a discriminated oneOf over the four stream variants: transfer.added, transfer.updated, transfer.removed, and sync.reset. The gate now fails if variant refs, discriminator mappings, required fields, singleton type enums, or closed-object metadata drift from the Rust/WebUI event contract.

2026-07-18 Progress - Live SSE Payload Schema Gate

Extended the persisted running-daemon REST conformance command so the /api/v1/events probe parses the first SSE frame's data: payload and validates it against the Rust OpenAPI TransferEvent schema component. This keeps the live resume sync.reset frame honest beyond text-snippet checks and reports schema failures as getEvents conformance failures.

Extended the static OpenAPI schema-component checker so TransferCreateRequest.link and TransferCreateRequest.links[] must document the REST validator's eD2K-only, no-whitespace, 2048-character link text contract, and the batch form must keep its 100-link ceiling.

2026-07-18 Progress - Static URL Import Schema Gate

Extended the static OpenAPI schema-component checker so UrlImportRequest.url must document the REST validator's case-insensitive HTTP(S), no-whitespace, host-required, 2048-character URL text contract.

2026-07-18 Progress - Static Endpoint Address Schema Gate

Extended the static OpenAPI schema-component checker so ServerCreateRequest and KadBootstrapRequest address fields must document the REST validator's non-empty-after-trim endpoint address contract with a machine-readable non-whitespace pattern.

2026-07-18 Progress - Static Friend Name Schema Gate

Extended the static OpenAPI schema-component checker so FriendCreateRequest.name must document the REST validator's 128-character, control-free display-name contract.

2026-07-18 Progress - Static Category Text Schema Gate

Extended the static OpenAPI schema-component checker so category create/patch name and path fields must document the REST validator's non-empty-after-trim text contract.

2026-07-18 Progress - Rust OpenAPI Conformance Pin

Pinned the persisted running-daemon REST conformance wrapper to load rest-api-smoke.py with EMULEBB_REST_OPENAPI_CONTRACT_PATH set to the Rust product OpenAPI artifact under docs/products/emulebb-rust/api. The wrapper restores any inherited operator value after import, but the smoke module's import-time route inventory and response validators now use the Rust-forward contract instead of the older shared REST contract by accident.

2026-07-19 Progress - Static Transfer Rename Schema Gate

Extended the static OpenAPI schema-component checker so TransferPatch.name must document the REST/core transfer rename contract: trim-non-empty text without eD2K/Windows-forbidden filename characters or control characters, and without an unsupported fixed max-length claim.

2026-07-19 Progress - Static Search Query Schema Gate

Extended the static OpenAPI schema-component checker so SearchCreateRequest.query must document the REST/WebUI search-query contract: ASCII-whitespace-normalized, non-empty text with a 160-character ceiling and no non-whitespace control characters.

2026-07-19 Progress - Static Shared Directory Root Schema Gate

Extended the static OpenAPI schema-component checker so both accepted SharedDirectoryRootInput forms, raw string and { path } object, must document the REST validator's trim-non-empty path contract.

Extended the static OpenAPI schema-component checker so transfer-create link text must document the full REST/WebUI input contract: case-insensitive ed2k://, no whitespace, no control characters, and a 2048-character ceiling.

2026-07-19 Progress - Static URL Import Control Schema Gate

Extended the static OpenAPI schema-component checker so UrlImportRequest.url must document the full REST/WebUI URL import contract: case-insensitive HTTP(S), host-required, no whitespace, no control characters, and a 2048-character ceiling.

2026-07-19 Progress - Static Category Selector Schema Gate

Extended the static OpenAPI schema-component checker so categoryName selectors on transfer create, transfer patch, and search-result download requests must document the REST selector contract: string input with at least one non-whitespace character after ASCII whitespace trimming.

2026-07-19 Progress - Static Priority Schema Gate

Extended the static OpenAPI schema-component checker so transfer, shared-file, server, and category priority request schemas must document the REST validator contracts: exact string vocabularies for transfer/shared-file/server controls, category string-or-u32 input, and shared-file rating bounds.

2026-07-19 Progress - Static Category Mutation Field Gate

Extended the static OpenAPI schema-component checker so category create/patch color and priority fields must document the REST validator contract: nullable RGB integer color bounds and the shared category priority input shape.

2026-07-19 Progress - Static Category Selector Exclusion Gate

Extended the static OpenAPI schema-component checker so category-selector request schemas must document that categoryId and categoryName are mutually exclusive, matching the REST boundary validator.

2026-07-19 Progress - Static Category Selector ID Schema Gate

Extended the static OpenAPI schema-component checker so category-selector categoryId request fields must document the REST validator's unsigned u32 integer contract across transfer create, transfer patch, and search-result download requests.

Extended the static OpenAPI schema-component checker so TransferCreateRequest must document the REST validator's request selector contract: exactly one single link or batch links array is required.

2026-07-19 Progress - Static Shared File Comment Rating Gate

Extended the static OpenAPI schema-component checker so SharedFilePatch must document the REST validator's comment/rating coupling: comment edits and rating edits must be submitted together.

2026-07-19 Progress - Static Paused Control Schema Gate

Extended the static OpenAPI schema-component checker so transfer-create and search-result download requests must document the REST validator's optional paused control as a boolean field.

2026-07-19 Progress - Static Shared File Comment Schema Gate

Extended the static OpenAPI schema-component checker so SharedFilePatch must document the REST validator's comment field as a string when submitting the comment/rating pair.

2026-07-19 Progress - Static Server Boolean Control Gate

Extended the static OpenAPI schema-component checker so server create/patch requests must document the REST validator's boolean control fields: static, connect, and enabled.

2026-07-19 Progress - Static Endpoint Port Schema Gate

Extended the static OpenAPI schema-component checker so server create and Kad bootstrap requests must document the REST validator's endpoint port contract: integer ports in the inclusive 1..65535 range.

2026-07-19 Progress - Static Endpoint Required Field Gate

Extended the static OpenAPI schema-component checker so server create and Kad bootstrap requests must document the REST validator's required endpoint fields: both address and port.

2026-07-19 Progress - Static Category Comment Schema Gate

Extended the static OpenAPI schema-component checker so category create/patch requests must document the REST validator's optional comment field as a string.

2026-07-19 Progress - Static Category Create Required Field Gate

Extended the static OpenAPI schema-component checker so category create requests must document the REST validator's required name field.

2026-07-19 Progress - Static Server Name Schema Gate

Extended the static OpenAPI schema-component checker so server create/patch requests must document the REST validator's optional name field as a string.

2026-07-19 Progress - Static URL Import Required Field Gate

Extended the static OpenAPI schema-component checker so URL import requests must document the REST validator's required url field.

2026-07-19 Progress - Static Search Create Required Field Gate

Extended the static OpenAPI schema-component checker so search create requests must document the REST validator's required query field.

2026-07-19 Progress - Static Search Mode Type Schema Gate

Extended the static OpenAPI schema-component checker so search create requests must document the REST validator's optional search method and type enum contracts.

2026-07-19 Progress - Static Search Extension Schema Gate

Extended the static OpenAPI schema-component checker so search create requests must document the REST validator's optional extension field as a string.

2026-07-19 Progress - Static Search Numeric Filter Schema Gate

Extended the static OpenAPI schema-component checker so search create requests must document the REST validator's optional numeric filters: unsigned minSizeBytes, unsigned maxSizeBytes, and minAvailability in the inclusive 0..1000000 range.

2026-07-19 Progress - Static Transfer Patch Mutation Family Gate

Extended the static OpenAPI schema-component checker so transfer patch requests must document the REST validator's one-mutation-family rule across priority, name, categoryId, and categoryName.

2026-07-19 Progress - Static Friend User Hash Schema Gate

Extended the static OpenAPI schema-component checker so friend create requests must document the REST validator's required userHash field as a lowercase 32-character hexadecimal string.

2026-07-19 Progress - Static Diagnostic Full Memory Schema Gate

Extended the static OpenAPI schema-component checker so diagnostic dump requests must document the REST validator's optional fullMemory control as a boolean field.

2026-07-19 Progress - Static Numeric Query Bounds Gate

Extended the static OpenAPI parameter checker so numeric query parameters must document the REST middleware bounds for limit, offset, and transfer-list categoryId.

2026-07-19 Progress - Static Boolean Query Type Gate

Extended the static OpenAPI parameter checker so optional boolean query controls must document the REST middleware type contract for includeScoreBreakdown, includeEvidence, and exactTotal.

2026-07-19 Progress - Static Transfer State Query Gate

Extended the static OpenAPI parameter/schema checker so the transfer-list state query filter must reference the shared TransferState schema and that schema must match the REST middleware's accepted state names.

2026-07-19 Progress - Static Numeric Path Bounds Gate

Extended the static OpenAPI parameter checker so numeric path parameters must document the REST middleware bounds for categoryId and searchId.

2026-07-19 Progress - Static Lowercase Hash Path Gate

Extended the static OpenAPI parameter checker so MD4-style path parameters must document the REST middleware's lowercase 32-character hexadecimal contract for hash and userHash.

2026-07-19 Progress - Static Endpoint Token Path Gate

Extended the static OpenAPI parameter checker so endpoint-shaped path parameters must document the REST middleware's address:port token contract for serverId and the MD4-or-endpoint contract for clientId.

2026-07-19 Progress - Static Logs Limit Default Gate

Split /logs onto a dedicated LogsLimit OpenAPI parameter and extended the static checker so the documented default matches the REST handler's 200-line default instead of the generic paged collection limit default.

2026-07-19 Progress - Static Search Status Reason Gate

Extended the static OpenAPI schema checker so the Search response schema must document the always-emitted statusReason field as a required nullable string, matching the create, list, and get search response mappers.

2026-07-19 Progress - Static Search Session Status Reason Gate

Extended the same schema checker coverage to SearchSession, so search list summaries also document the always-emitted nullable statusReason field.

2026-07-19 Progress - Static Search Status Enum Gate

Extended the static OpenAPI schema checker so Search.status and SearchSession.status document the Rust mapper's emitted status vocabulary, including the additive queued state used while network searches wait to run.

2026-07-19 Progress - Static Search Result Extension Gate

Extended the static OpenAPI schema checker so SearchResult.extension is a required string, matching the search result response mapper that always emits the derived filename extension.