API changelog and migration guides

Release notes for compatible v1 changes, deprecation notices, and migration guide templates live beside OpenAPI, the reference, and SDK source so clients can track contract history as well as endpoints.

Release notes

Every compatible public API release gets an entry with affected operationIds, additive fields, documentation examples, and validation gates. Breaking removal requires a new major version or a completed deprecation path.

Change classification

Every entry uses one compatibility category so integrators can separate routine additions, migrations, major-version work, and security action.

Additive
Compatible optional fields or operations with synchronized OpenAPI, SDK, examples, and tolerant-client evidence.
Deprecated
The old flow remains available during its notice window while replacement, telemetry, migration, and support guidance are published.
Breaking or new major
An incompatible removal or semantic change ships only in a new major version or after the documented deprecation path is complete.
Security
Security notes state impact and required client action without exploitable detail. Unsafe compatibility follows the emergency migration process.
Stable candidateAdditive

SLO, freshness, and capacity budgets

The Public API now has numeric product SLOs, strong If-Match on mutable writes, and admission budgets before provider signing.

  • Targets are availability ≥99.9%, latency p95 ≤2000 ms, catalog p95 ≤15 s, search index p95 ≤1800 s, RPO ≤24 h, and RTO ≤4 h; warning/critical alerts fire before breach.
  • Mod and ModVersion PATCH require strong If-Match and return a new ETag. Missing returns 428; malformed, weak, wildcard, or list values return 412 without mutation; a valid stale value returns 412 with the current ETag. Retained SDKs preserve Problem Details and status.
  • Multipart now defaults to 16 MiB × 4 after a live R2 capacity probe. Primary and fallback download admission require authoritative size and enforce actor/client concurrency plus byte buckets before expensive signing; leases recover after failure or expiry.
Stable candidateAdditive

Universal domain IDs and lifecycle

All thirteen public entities now have uniform opaque UUIDs, ownership/state rules, tombstone/410 non-reuse, and an immutable published-version snapshot without removing legacy aliases.

  • Game, Mod, ModVersion, Artifact, Blob, UploadSession, DownloadGrant, User, Team, Dependency, Job, WebhookSubscription, and WebhookDelivery have separate UUIDs and fail-closed state machines. Rename preserves identity; a slug, filename, URL, storage key, ETag, or hash does not identify a resource.
  • Archive can be restored; permanent deletion leaves a retained tombstone and returns 410. Neither UUID nor source binding is ever reused; an unknown ID returns 404.
  • First publication captures a canonical snapshot and SHA-256. Semantic fields are immutable; restore requires the exact snapshot and current upload-trust gates. lifecycle_version protects concurrent updates. Old uploaded/quarantined/approved/rejected values remain compatibility aliases.
Stable candidateAdditive

Fail-closed compensation boundaries

PR-F2.6.6 and PR-F2.6.7 define replay and recovery for provider upload commit, ready binding, publish/outbox, and trust-enqueue checkpoints without weakening trust or publication preconditions.

  • No cross-provider transaction is claimed. A once-only generation byte-budget checkpoint precedes an atomic session/file/upload.accepted reservation; replay between them reuses the same budget result. Every single PUT returns signed If-None-Match: * and a maximum 900-second URL lease, row-lock checkpointed after signing and before response; an abort-winning checkpoint rejects the URL. A conditional 412 proceeds to complete for authoritative HEAD/seal/SHA-256 verification. A session-level claim then conditionally copies the exact client-source ETag into a write-once server-only seal before the queued scan checkpoint.
  • Heartbeat-backed claim tokens fence every scan mutation. Ready, quarantine, and rejection each commit session/file/job state, audit, and their canonical event in one database transaction; exact terminal replay reloads and validates that envelope. Before provider configuration or normal claims, a bounded database-only sweep recovers stale max-attempt claims without rerunning scan/provider work: coherent state atomically enters canonical manual review/quarantine, while incoherent state terminal-fails only the exact job for visible manual intervention. Same-SHA final promotion uses a durable SHA/bucket/key claim, canonical MIME, and cf-copy-destination-if-none-match: *; a fresh duplicate waits or adopts the ready blob without consuming a normal scan attempt. Polling is exponential and bounded to 15 minutes per claim. The first two durable token-fenced yields atomically requeue with database-owned 30-to-60-second jitter and restore the normal attempt; the third atomically creates manual review and quarantine, with exact lost-response replay, instead of retrying forever. Lost copy response recovery requires exact HEAD and a nonblank ETag. A content-addressed final copy becomes ready only after an accessible attested blob and atomic ready/audit/file.ready binding. Transactional publish and its durable outbox also share one transaction. Quarantine scan.completed and later file.ready keep distinct identities. Account-inbox notification is not the lifecycle acknowledgement.
  • After a lost response, the same Idempotency-Key reacquires only the same generation claim and converges without duplicate budget, audit, job, terminal-event, or outbox effects. For single cleanup, durable stage 1 conditionally writes a zero-byte tombstone and verifies provider ETag, LastModified, metadata token, and size=0; the fence is held through the latest URL lease and at least 16 minutes. Stage 2 exact-deletes and HEAD-verifies staging before DB completion; crash replay resumes the stage. Multipart cleanup aborts and relists exact-key uploads, every seal claim key is deleted and verified, and quarantine is retained. Desired CORS includes If-None-Match, and the object-level probe verifies conditional copy plus late-PUT tombstone rejection, but live CORS/lifecycle/lock evidence remains BLOCKED pending CLOUDFLARE_API_TOKEN, MODDINGFLOW_R2_FINAL_LOCK_MIN_SECONDS, and MODDINGFLOW_R2_ALLOWED_BROWSER_ORIGINS; no live deployment is claimed. An unbound final copy remains non-ready and never publishable until PR-F3.13 reference-aware legal-hold-safe deletion. Numeric freshness remains PR-F2.6.3, and deployed webhook enablement remains PR-F8.14.
Stable candidateAdditive

Managed artifact expansion

Optional PublicModVersion.artifacts adds complete metadata for a managed primary artifact while keeping artifact_ids as the stable v1 linkage.

  • Expansion is available only with size_bytes > 0, exact sha256/hashes.sha256, a current upload-session match on file_id and final_blob_id, actual AV scan_status=clean, lifecycle=ready, publication state=published, and managed resolve eligibility.
  • The complete artifact includes role/category, display and nullable platform hints, integrity fields, actual AV state, and provider-opaque download_metadata with a relative resolve_endpoint and truthful range_supported value.
  • For a legacy artifact, artifacts is omitted while artifact_ids remain available; the API does not fabricate metadata or expose a provider URL.
  • PR-F2.6.4 defines bounded freshness: a version write is committed private/no-store while anonymous catalog/version GET uses an eventual shared cache; cached PublicModVersion artifact expansion requires scan_status=clean, lifecycle=ready, and publication state=published and remains eventual; direct resolve/install plan reads are private point-in-time reads over an eligible target whose established state may be approved or published. The current inventory contains 53 approved public legacy targets and 4 published targets, so direct eligibility is not published-only and not legacy-only. Upload status is current database state at request time, worker transitions are eventual, and progress.available=false means unknown; an install plan is a deterministic private/no-store request-time snapshot; webhooks are feature-gated, eventual, at-least-once, replayable, and unordered, with no delivery promise while disabled. Numeric catalog convergence remains PR-F2.6.3, and live webhook rollout remains PR-F8.14.
Stable candidateAdditive

Download integrity boundary

OpenAPI now distinguishes the stored-object SHA-256 from a transport representation validator without presenting ETag as a content hash.

  • sha256 and hashes.sha256 cover the complete immutable stored object; clients compare size_bytes, assemble every Range part, and only then verify SHA-256.
  • ETag is an opaque non-cryptographic validator for one transport representation, is not file identity, and may change across primary, refreshed, and fallback providers.
  • The current signed transport does not guarantee RFC 9530 Content-Digest or Repr-Digest; fail-closed OpenAPI lint rejects false header or transcoding claims.
Stable candidateAdditive

Async upload completion acceptance

Upload completion now distinguishes accepted background work from an already terminal result while keeping the same response DTO and idempotent side effects.

  • POST /v1/uploads/{upload_id}/complete returns 202 Accepted for queued or processing jobs and requires Location to equal data.job.statusEndpoint.
  • A terminal or synchronous result returns 200 OK without Location; replay with the original Idempotency-Key preserves the committed status and header without repeating storage, audit, or webhook effects.
  • The additive status is covered by generated OpenAPI, curated examples, lint failures, and retained TypeScript, Python, and C# client probes.
Beta toolingAdditive

Official CLI and SDK compile gate

The source distribution now includes an official Node.js CLI for creator upload automation and an explicit compile gate for every generated SDK language.

  • npm run api:cli exposes credential check, archive validation, upload create/resume/complete/status, and a complete API-key-to-publish command without accepting secrets in argv.
  • Production, local sandbox, and operator-provided protected staging base URLs are selected through environment profiles; staging uses MODDINGFLOW_STAGING_API_BASE_URL and a staging-only key.
  • npm run api:sdks:compile type-checks a real TypeScript consumer, imports the Python package without bytecode writes, and builds the C# smoke consumer as part of npm run api:openapi:check.
Stable candidateAdditive

Stable v1 OpenAPI documentation gate

OpenAPI 3.1 remains the source of truth for auth, scopes, errors, cursor pagination, upload sessions, download grants, webhooks, SDK source artifacts, and public reference rendering.

  • Successful request/response examples cover install plan resolve, upload-session create/status/parts/complete/abort, download resolve/fallback, and webhook subscription/delivery/redelivery flows.
  • Problem Details examples use application/problem+json with type, title, status, detail, instance, code, request_id, and trace_id so clients do not parse prose.
  • Webhook verification guidance documents X-Moddingflow-Signature, X-Moddingflow-Timestamp, X-Moddingflow-Delivery-Id, and HMAC-SHA256 over `${timestamp}.${raw_body}`.
Policy activeDeprecated

Compatibility, Deprecation, and Sunset policy

Stable v1 old clients keep working during the support window while additive changes can ship through documented, tolerant OpenAPI, SDK, and docs updates.

  • Deprecated OpenAPI operations must include x-deprecation-date, x-sunset-date, x-migration-url, and x-changelog-url.
  • HTTP-visible deprecations use Deprecation, Sunset, and Link rel="deprecation" headers.
  • Minimum notice is 90 days or two stable API releases, whichever gives the safer migration window.
  • The repository release gate requires committed migration evidence, a ready non-deprecated replacement, dated telemetry review, and an approved support plan with explicit owner and approver before Sunset.

Migration guide templates

Every deprecation guide must be actionable: what to replace, how to find affected clients, how to roll out the replacement, and what support evidence to include.

Deprecated operation template

Deadline
No earlier than x-sunset-date and at least 90 days or two stable API releases after x-deprecation-date.
Replacement
Name the replacement operationId/path, required scopes, request/response schema differences, and expected Problem Details codes.
Detection
Search for the deprecated operationId in SDK calls, OpenAPI client generation, logs, user-agent/client_id traffic, and stored integration configs.
Rollout
Add the replacement client path, enable dual-read or dual-write if needed, verify idempotency/cursor behavior, then remove the old operation after sunset.
Support
Include X-Request-Id, request_id, trace_id, client_id, affected operationId, and SDK version in support requests.

First stable v1 limitations

Deadline
Before stable launch, limitations must be written as release notes instead of hidden implementation details.
Replacement
If a capability is still beta or experimental, mark x-moddingflow-stage=beta or x-moddingflow-stage=experimental and keep it out of stable flow prerequisites.
Detection
Compare public OpenAPI paths, generated SDK clients, localized docs, FAQ, and runtime route parity before publishing.
Rollout
Publish only after npm run api:openapi:check, npm run locales:validate, and focused docs/reference rendering tests pass.
Support
Known limitations must point to a changelog entry, migration guide, or support contact instead of an informal agreement.

Release validation evidence

  • npm run api:openapi:generate -- --check confirms committed JSON matches the Rust generator.
  • npm run api:openapi:lint checks examples, auth, scopes, Problem Details, pagination, uploads, downloads, webhooks, and deprecation metadata.
  • npm run api:sdks:check confirms generated TypeScript, Python, and C# source artifacts match OpenAPI operationIds and schemas.
  • npm run locales:validate keeps API docs, reference, SDK, FAQ, and changelog copy synchronized across ru, en, and de.