Spaces:
Sleeping
Sleeping
|
Download docs/architecture/API_CONTRACT_V1.md from phucsd/Oloka-Video-Dev: direct link, hf CLI and curl.
- Browser
- Download file 30.6 kB
-
https://huggingface.co/spaces/phucsd/Oloka-Video-Dev/resolve/main/docs/architecture/API_CONTRACT_V1.md
- Command line
-
hf download hf://spaces/phucsd/Oloka-Video-Dev/docs/architecture/API_CONTRACT_V1.md
-
curl -L -o API_CONTRACT_V1.md https://huggingface.co/spaces/phucsd/Oloka-Video-Dev/resolve/main/docs/architecture/API_CONTRACT_V1.md
30.6 kB
| # API Contract V1 | |
| Status: capability map and transport contract. Slice 3C, Slice 3D, and the schema-v6 Phase 3E core are implemented, merged, and production-cutover complete; the Phase 3E.3 transactional-outbox runtime correction is not deployed until its later cutover verification. Phase 3F has not started. | |
| ## Protocol conventions | |
| - Product API paths are `/api/v1/**`. Foundation probes remain `/api/health`, `/api/ready`, and `/api/version`. | |
| - JSON contracts are strict Zod schemas exported once from `packages/contracts`; unknown keys fail with the canonical error envelope from `ERROR_MODEL.md`. | |
| - UUID resource IDs are opaque. Unauthorized private resources are indistinguishable from missing resources (`404 RESOURCE_NOT_FOUND`). | |
| - All unsafe cookie-authenticated requests apply `AUTH_SESSION_CSRF.md`. | |
| - First-admin bootstrap is part of the verified Google callback transaction and | |
| has no separate HTTP route. Admin role/status mutation enforces the | |
| last-active-admin guard; emergency recovery is operator maintenance only. | |
| - Important mutations require exactly one `Idempotency-Key` containing 1-128 | |
| ASCII characters from `A-Z a-z 0-9 . _ : -`. Missing, empty, multiple, | |
| Unicode, whitespace, control, slash, backslash, quote, or oversized values | |
| are `400 VALIDATION_ERROR`. The server hashes the key and canonical semantic | |
| request; same key/same request replays the stored result and same key/different | |
| request is `409 IDEMPOTENCY_CONFLICT`. | |
| - Mutable resources expose an integer `version` and ETag. Updates require `If-Match` or `expectedVersion`; stale writes return `409 VERSION_CONFLICT` with current safe version, never silently overwrite. | |
| - Lists use an opaque base64url cursor containing versioned canonical sort position, filter fingerprint, and ID tie-breaker. Default/max page sizes are 25/100. Sorting is deterministic; cursor/filter mismatch is `400 INVALID_CURSOR`. | |
| - Times are ISO 8601 UTC strings at the HTTP boundary and epoch milliseconds in SQLite. | |
| - IDs, not storage keys/paths/provider secrets, cross the API boundary. | |
| ### Slice 3C Project projection | |
| Owner Project responses expose only `id`, `name`, nullable `description`, | |
| `favorite`, `status`, ISO-8601 UTC `createdAt`/`updatedAt`, and positive | |
| `version`. They never expose owner columns, retention/purge internals, storage | |
| keys, or filesystem paths. Owner routes return `404 RESOURCE_NOT_FOUND` for a | |
| different owner, including when the actor is an admin. | |
| Slice 3C lists only `active` Projects at API-011 and only `soft_deleted` | |
| Projects at API-062. Both use the authenticated opaque cursor contract and the | |
| owner list order `favorite DESC, updated_at DESC, id DESC`. Slice 3C does not | |
| implement purge scheduling/worker side effects despite the later-state schema | |
| foundation. | |
| ### Slice 3D Asset projection | |
| Owner Asset responses expose only the resource/project IDs, normalized original | |
| filename, kind, declared/verified MIME, immutable byte size/checksum, bounded | |
| technical metadata, ingestion/lifecycle status, safe failure code, timestamps, | |
| and optimistic version. They never expose owner columns, storage/staging keys, | |
| filesystem paths, reconciliation state, or capability hashes. Upload responses | |
| expose the UploadSession ID, Asset ID, canonical received offset, declared | |
| length, lifecycle status, expiry, and recommended chunk size only. | |
| ## Common errors | |
| The stable code, status, retryability, message key, action, severity, and allowed | |
| surface come only from `ERROR_MODEL.md`. Common mappings are | |
| `400 VALIDATION_ERROR`/`INVALID_CURSOR`, `401 AUTHENTICATION_REQUIRED`, | |
| `403 AUTHORIZATION_DENIED` or an account-status code, | |
| `404 RESOURCE_NOT_FOUND`, `409 RESOURCE_STATE_CONFLICT`/`VERSION_CONFLICT`/ | |
| `IDEMPOTENCY_CONFLICT`, `413 PAYLOAD_TOO_LARGE`, | |
| `415 UNSUPPORTED_MEDIA_TYPE`, `416 RANGE_NOT_SATISFIABLE`, typed 422 domain | |
| codes, `429 QUOTA_EXCEEDED`/`RATE_LIMITED`/`PROVIDER_RATE_LIMITED`, typed | |
| 502/503 provider or storage codes, and `500 INTERNAL_ERROR`. Every response | |
| contains safe `code`, localized `messageKey`, `suggestedAction`, `retryable`, | |
| `requestId`, and optional allowlisted field details; never a stack or provider body. | |
| ## Capability map | |
| Legend: `U` authenticated user/owner, `A` administrator, `P` public/operator probe, `O` OIDC provider callback. `Idem` means `Idempotency-Key` required. `Ver` means optimistic version required. “Audit” names the success/denial class; security denials may be sampled/rate-limited. | |
| | ID | Method and path | Actor; request -> response | Errors | Idempotency/concurrency | Audit; transaction/side effect | | |
| | ------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------ | ------------------------------------------------------------ | | |
| | API-001 | `GET /api/health` | P; none -> liveness/build | 500 | none/read-only | metric only; no tx | | |
| | API-002 | `GET /api/ready` | P/operator; none -> DB/storage/migration readiness without secrets | 503 | none/read-only | operational log; no write tx | | |
| | API-003 | `GET /api/version` | P; none -> build SHA/schema compatibility | 500 | cache-safe/read-only | metric only; no tx | | |
| | API-004 | `GET /api/v1/auth/google/start` | browser; safe same-origin return path -> Google redirect | 400,429,503 | one-use OAuth transaction | login-start; short security-state write | | |
| | API-005 | `GET /api/v1/auth/google/callback` | O/browser; code/state -> session cookie + validated return path; every failure -> fixed `303 /auth/error` without query disclosure | fixed safe redirect | state/nonce/PKCE one-use | login success/failure; provider denial consumes state; TX-01 | | |
| | API-006 | `GET /api/v1/auth/session` | U/pending member; none -> safe user status/session expiry | 401 | read-only | none; session last-seen coalesced | | |
| | API-007 | `GET /api/v1/auth/csrf` | U/pending member; none -> synchronizer token | 401 | no-store; per session | none; may rotate token in a guarded session write | | |
| | API-008 | `POST /api/v1/auth/logout` | U/pending member; CSRF -> 204 | 401,403 | repeat succeeds | logout; TX-03 | | |
| | API-009 | `GET /api/v1/auth/sessions` | U; cursor -> own safe session list | 401,400 | cursor/read-only | none | | |
| | API-010 | `POST /api/v1/auth/sessions/revoke-all` | U; CSRF/confirm -> revoked count + current-session policy result | 401,403 | Idem | session revoke-all; TX-03 | | |
| | API-011 | `GET /api/v1/projects` | U; cursor/status -> project summaries | 400,401 | cursor/read-only | none | | |
| | API-012 | `POST /api/v1/projects` | U; name/description -> project | 400,409 | Idem | project.create; TX-04 | | |
| | API-013 | `GET /api/v1/projects/:projectId` | U owner; ID -> project snapshot | 401,404 | ETag/read-only | none | | |
| | API-014 | `PATCH /api/v1/projects/:projectId` | U owner; rename/favorite + expected version -> updated project | 400,401,404,409 | Idem + Ver | project.update; TX-05 | | |
| | API-015 | `DELETE /api/v1/projects/:projectId` | U owner; expected version/confirm -> soft-deleted project | 401,404,409 | Idem + Ver | project.delete_requested; TX-06, later queues purge | | |
| | API-016 | `POST /api/v1/projects/:projectId/restore` | U owner; expected version -> active project when policy permits | 401,404,409 | Idem + Ver | project.restore; TX-06 | | |
| | API-017 | `POST /api/v1/projects/:projectId/uploads` | U owner; file metadata/size/checksum -> Asset + upload session | 400,404,409,413,415 | Idem; project Ver guard | upload.create; TX-07 | | |
| | API-018 | `HEAD /api/v1/uploads/:uploadId` | U owner; ID -> offset/length/status headers | 401,404,409 | read-only authoritative offset | none | | |
| | API-019 | `PATCH /api/v1/uploads/:uploadId` | U owner; offset + binary chunk -> new offset | 400,404,409,413,415 | offset/chunk checksum retry; Ver | upload.append metric; file then TX-08 | | |
| | API-020 | `POST /api/v1/uploads/:uploadId/complete` | U owner; optional checksum -> asset/job | 404,409,415,422 | Idem + upload Ver | upload.complete; TX-09 | | |
| | API-021 | `DELETE /api/v1/uploads/:uploadId` | U owner; ID -> 204 | 404 | repeat succeeds | upload.abort; TX-10 | | |
| | API-022 | `GET /api/v1/projects/:projectId/assets` | U owner; cursor/media type/upload time/ingestion/lifecycle/original-filename search -> assets | 400,404 | cursor/read-only | none | | |
| | API-023 | `GET /api/v1/assets/:assetId` | U owner; ID -> metadata/status | 401,404 | ETag/read-only | none | | |
| | API-024 | `POST /api/v1/assets/:assetId/retry-ingestion` | U owner; failed active Asset/version -> same Asset + new ingestion attempt | 404,409 | Idem + Ver | asset.ingestion_retry; TX-10-style admission | | |
| | API-025 | `DELETE /api/v1/assets/:assetId` | U owner; expected version -> soft-deleted lifecycle | 404,409 | Idem + Ver | asset.delete_requested; later TX-20 schedule | | |
| | API-026 | `GET /api/v1/assets/:assetId/content` | U owner; Range/conditional headers -> bytes | 404,416,503 | immutable ETag/read-only | access metric; authorized range stream | | |
| | API-027 | `POST /api/v1/assets/:assetId/delivery-capabilities` | U owner; operation -> short opaque capability | 404,409 | Idem; resource state guard | capability.issue; TX-23 | | |
| | API-028 | `GET /api/v1/projects/:projectId/compositions` | U owner; cursor/status -> version summaries | 400,404 | cursor/read-only | none | | |
| | API-029 | `POST /api/v1/projects/:projectId/compositions` | U owner; CompositionDocumentV1/parent/version -> immutable version | 400,404,409,422 | Idem + project Ver | composition.create; TX-11 | | |
| | API-030 | `GET /api/v1/compositions/:compositionVersionId` | U owner; ID -> canonical document/validation | 404 | immutable ETag | none | | |
| | API-031 | `POST /api/v1/compositions/:compositionVersionId/derive` | U owner; structured patch/edit intent -> new version | 400,404,409,422 | Idem + expected parent/project Ver | composition.derive; TX-11 | | |
| | API-032 | `POST /api/v1/compositions/:compositionVersionId/validate` | U owner; ID -> durable validation result | 404,409,422 | Idem; immutable input | composition.validate; bounded job or deterministic service | | |
| | API-033 | `POST /api/v1/projects/:projectId/generation-jobs` | U owner; brief/options/asset IDs -> Job | 400,404,409,422,503 | Idem + project Ver | generation.request; TX-12 | | |
| | API-034 | `POST /api/v1/compositions/:compositionVersionId/regenerate` | U owner; structured change brief/options -> Job | 400,404,409,422,503 | Idem; immutable source + project Ver | generation.regenerate; TX-12 | | |
| | API-035 | `POST /api/v1/compositions/:compositionVersionId/preview` | U owner; ID -> preview artifact/status | 404,409,422 | Idem; immutable version | preview.request/materialize; TX-12/TX-17 | | |
| | API-036 | `GET /api/v1/previews/:previewArtifactId` | U owner; ID -> artifact metadata/player bootstrap | 404,409,503 | immutable ETag/read-only | preview.access; authorized bootstrap | | |
| | API-037 | `GET /api/v1/previews/:previewArtifactId/content` | U owner or scoped capability; Range -> immutable artifact bytes | 404,416,503 | ETag/read-only | safe access metric; range stream | | |
| | API-038 | `POST /api/v1/compositions/:compositionVersionId/render-jobs` | U owner; output preset -> Job | 400,404,409,422,503 | Idem; immutable composition | render.request; TX-12 | | |
| | API-039 | `GET /api/v1/jobs` | U; cursor/type/status/project -> summaries | 400 | cursor/read-only | none | | |
| | API-040 | `GET /api/v1/jobs/:jobId` | U owner; ID -> durable snapshot | 404 | ETag/read-only | none | | |
| | API-041 | `GET /api/v1/jobs/:jobId/steps` | U owner; cursor -> safe step summaries | 400,404 | cursor/read-only | none | | |
| | API-042 | `GET /api/v1/jobs/:jobId/event-history` | U owner; cursor -> durable events | 400,404 | cursor/read-only | none | | |
| | API-043 | `GET /api/v1/jobs/:jobId/events` | U owner; Last-Event-ID -> SSE | 400,404,409 | durable replay; no mutation | connection metric; no tx beyond reads | | |
| | API-044 | `POST /api/v1/jobs/:jobId/cancel` | U owner; cancellable job/version -> snapshot | 404,409 | Idem + Ver | job.cancel_requested; TX-16 | | |
| | API-045 | `GET /api/v1/projects/:projectId/outputs` | U owner; cursor/state -> output summaries | 400,404 | cursor/read-only | none | | |
| | API-046 | `GET /api/v1/outputs/:outputId` | U owner; ID -> immutable evidence + lifecycle | 404 | ETag/read-only | none | | |
| | API-047 | `GET /api/v1/outputs/:outputId/content` | U owner/capability; Range -> video bytes | 404,416,503 | immutable ETag | output.download/stream metric; no write tx | | |
| | API-048 | `POST /api/v1/outputs/:outputId/delivery-capabilities` | U owner; download/stream -> opaque capability | 404,409 | Idem | capability.issue; TX-23 | | |
| | API-049 | `DELETE /api/v1/outputs/:outputId` | U owner; expected state version -> purge-scheduled | 404,409 | Idem + Ver | output.delete_requested; TX-20 | | |
| | API-050 | `GET /api/v1/settings/profile` | U; none -> safe profile/preferences | 401 | ETag/read-only | none | | |
| | API-051 | `PATCH /api/v1/settings/profile` | U; display/preferences/version -> profile | 400,409 | Idem + Ver | user.profile_update; short user tx | | |
| | API-052 | `GET /api/v1/settings/provider-status` | U; none -> enabled capability health (no reference names) | 401 | short cache/read-only | none | | |
| | API-053 | `GET /api/v1/admin/users` | A; cursor/status/search -> safe users | 400,403 | cursor/read-only | admin.read_users | | |
| | API-054 | `PATCH /api/v1/admin/users/:userId` | A; role/status/version -> user; reject last-active-admin lockout | 400,403,404,409 | Idem + Ver | admin.user_update; TX-02 | | |
| | API-055 | `GET /api/v1/admin/jobs` | A; cursor/type/status/lease -> redacted operational summaries | 400,403 | cursor/read-only | admin.read_jobs | | |
| | API-056 | `POST /api/v1/admin/jobs/:jobId/reconcile` | A; reason/version -> redacted reconciliation intent | 403,404,409 | Idem + Ver | admin.job_reconcile; TX-13/outbox | | |
| | API-057 | `GET /api/v1/admin/provider-credentials` | A; cursor -> reference names/status/health | 403 | cursor/read-only | admin.read_provider_refs | | |
| | API-058 | `PUT /api/v1/admin/provider-credentials/:referenceId` | A; provider/purpose/env-name/status/version -> reference | 400,403,409 | Idem + Ver | admin.provider_ref_update; never writes HF secret | | |
| | API-059 | `GET /api/v1/admin/audit-events` | A; cursor/actor/action/resource/time -> redacted events | 400,403 | cursor/read-only | admin.read_audit | | |
| | API-060 | `GET /api/v1/admin/operations` | A; none -> queue/lease/outbox/storage/backup health summary | 403,503 | read-only snapshot | admin.read_operations | | |
| | API-062 | `GET /api/v1/trash/projects` | U; cursor -> own soft-deleted/purge-scheduled Projects | 400,401 | cursor/read-only | none | | |
| | API-063 | `GET /api/v1/projects/:projectId/compositions/current` | U owner; project ID -> selected immutable version or null | 404 | project ETag/read-only | none | | |
| | API-064 | `POST /api/v1/outputs/:outputId/pin` | U owner; state version -> pinned state | 404,409 | Idem + Ver | output.pin; guarded output-state tx | | |
| | API-065 | `POST /api/v1/outputs/:outputId/unpin` | U owner; state version -> unpinned state | 404,409 | Idem + Ver | output.unpin; guarded output-state tx | | |
| | API-066 | `GET /api/v1/admin/users/pending` | A; cursor -> pending members | 400,403 | cursor/read-only | admin.read_pending_users | | |
| | API-067 | `POST /api/v1/admin/provider-credentials/:referenceId/health-check` | A; reference/version -> safe health result/job | 403,404,409,503 | Idem + Ver | admin.provider_health_check; bounded outbox/job intent | | |
| | API-068 | `GET /api/v1/admin/quota-policies` | A; cursor/scope -> versioned policies | 400,403 | cursor/read-only | admin.read_quota_policies | | |
| | API-069 | `POST /api/v1/admin/quota-policies` | A; scope/policy/effective interval -> new policy version | 400,403,409 | Idem + expected prior Ver | admin.quota_policy_create; quota/audit tx | | |
| | API-070 | `POST /api/v1/jobs/:jobId/retry` | U owner; retryable terminal job -> new Job | 404,409,503 | Idem; immutable original request | job.retry; TX-12, never reopens terminal row | | |
| | API-071 | `GET /api/v1/admin/jobs/:jobId` | A; ID -> redacted state/step/code/timestamps/lease diagnostics | 403,404 | ETag/read-only | admin.read_job_diagnostics; no owner SSE/content | | |
| | API-072 | `GET /api/v1/outputs/:outputId/download` | U owner or download-scoped capability; conditional request -> attachment bytes | 404,416,503 | immutable ETag/read-only | output.download metric; authorized Range stream | | |
| ## Cursor shape | |
| The opaque cursor encodes versioned canonical sort position, filter hash, and | |
| UUID tie-breaker as base64url JSON plus HMAC-SHA256 under an HKDF-derived app | |
| key. The implemented admin-user cursor is `{v:1, sort:{createdAt,id}, | |
| filterHash}` and binds normalized status/search. It never contains email, | |
| display name, secret values, storage keys, provider operation IDs, or token | |
| material. Invalid encoding/MAC/version/key/filter is `400 INVALID_CURSOR`; | |
| default/max page sizes are 25/100. | |
| ## Contract ownership and evolution | |
| Request, response, event, cursor, error, and composition schemas live in `packages/contracts` and are imported by both Fastify and React. API v1 additions are backward compatible; breaking shape/meaning requires a new API or schema version. OpenAPI may be generated from the canonical Zod contracts in Phase 3, but generated output is not a competing source of truth. | |
| The capability map contains 72 sequential capabilities. Render cancellation and | |
| retry use only the generic Job commands; no parallel render-specific command | |
| routes exist. Owner-facing Project, CompositionVersion, Job history/status and | |
| SSE routes are owner-only. Administrators inspect Jobs only through the redacted | |
| admin list/detail/command surfaces, which exclude prompts, private bytes, | |
| storage paths/keys, raw provider payloads, and owner SSE. | |