Define API and live update contract
This commit is contained in:
parent
0b2b026283
commit
8c0ec9627e
1 changed files with 954 additions and 0 deletions
71
docs/spec/api/README.md
Normal file
71
docs/spec/api/README.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Remora API contract semantics
|
||||
|
||||
OpenAPI `3.2.0` describes HTTP operations and JSON Schema Draft 2020-12
|
||||
describes WebSocket frames. The API contract version is `1.0.0`; additive
|
||||
fields require clients to tolerate unknown response fields only after a
|
||||
contract version that permits them, while incompatible changes use a new major
|
||||
path or media type.
|
||||
|
||||
## Idempotency
|
||||
|
||||
Every public mutation requires `Idempotency-Key`. The identity is caller
|
||||
subject, operation, and key. The server canonicalizes the semantic request,
|
||||
including the authorization target, with RFC 8785 and stores its SHA-256
|
||||
fingerprint and complete response atomically with the mutation. The default
|
||||
retention is 24 hours and is never shorter than the advertised
|
||||
`Idempotency-Expires-At`.
|
||||
|
||||
The same identity and fingerprint replays the original status, resource, and
|
||||
body after duplicate or ambiguous delivery. A different fingerprint returns
|
||||
`idempotency_conflict` and cannot affect either request. Naturally idempotent
|
||||
deletion and cancellation semantics still use this request identity.
|
||||
|
||||
Internal claim uses job ID plus `operation_id` because no attempt exists yet.
|
||||
Renew, progress, and outcome use attempt ID, fencing token, worker incarnation,
|
||||
and `operation_id`. A replay has the original effect; a different, stale, or
|
||||
non-authoritative tuple returns `precondition_failed` without mutation.
|
||||
|
||||
## Authorization and errors
|
||||
|
||||
Public operations apply the authorization rules in the architecture. List is
|
||||
filtered before pagination. A missing and an unauthorized resource both return
|
||||
`not_found`; callers cannot probe job existence. Cursors and event replay are
|
||||
bound to the authenticated subject and filter.
|
||||
|
||||
`ApiError` fields are an allowlist. Messages and field diagnostics never carry
|
||||
credential values, bearer material, URL userinfo, raw upstream response bodies,
|
||||
internal stack traces, or unredacted configured endpoints.
|
||||
|
||||
## Jobs, pages, and results
|
||||
|
||||
`Job.phase` uses the exact `JobProtocol` phase vocabulary; this contract does
|
||||
not define phase transitions. `lifecycle`, current authoritative attempt,
|
||||
current matching progress, reusable-checkpoint projection, result, and failure
|
||||
are separate fields. Progress is present only when its attempt ID and fence
|
||||
match the authoritative attempt in the same projection.
|
||||
|
||||
Job pages order by `(created_at DESC, id DESC)`. The first request fixes a
|
||||
membership snapshot, authorization/filter fingerprint, and high-water mark.
|
||||
`next_cursor` is an opaque integrity-protected keyset cursor for that snapshot;
|
||||
new jobs do not enter later pages. Invalid, altered, cross-subject, or
|
||||
cross-filter cursors return the corresponding structured cursor error.
|
||||
|
||||
A result endpoint returns only a visible `CommittedResult`. Tombstoned,
|
||||
unauthorized, absent, and never-committed results are not exposed. The hard
|
||||
delete endpoint returns the durable deletion resource established by the
|
||||
architecture.
|
||||
|
||||
## WebSocket replay
|
||||
|
||||
An HTTP job or list snapshot carries `event_cursor` at its consistency
|
||||
watermark. The client connects to `/v1/events` with that cursor. Server frames
|
||||
are ordered by increasing `sequence` for a connection and carry an opaque next
|
||||
cursor. Delivery is at least once; clients deduplicate event IDs and ignore a
|
||||
job revision no newer than the applied revision.
|
||||
|
||||
Replay applies current authorization before delivery. If a cursor is absent,
|
||||
invalid, expired, authorization-incompatible, or no longer available after
|
||||
coordination loss, the server sends `resync_required`; the client obtains a new
|
||||
HTTP snapshot and reconnects from its cursor. Progress frames for a stale
|
||||
attempt/fence are suppressed before emission and ignored defensively by the
|
||||
client.
|
||||
Loading…
Reference in a new issue