2026-07-13 14:59:50 +09:00
|
|
|
# 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,
|
2026-07-13 17:23:32 +09:00
|
|
|
and `operation_id`. Each operation identity retains a request fingerprint. An
|
|
|
|
|
exact replay has the original effect; a changed worker, fence, payload, stale,
|
|
|
|
|
or non-authoritative tuple returns `precondition_failed` without mutation.
|
2026-07-13 14:59:50 +09:00
|
|
|
|
2026-07-13 18:23:03 +09:00
|
|
|
[`RemoraApiMutation`](../../protocol/RemoraApiMutation.tla) models this durable
|
|
|
|
|
operation ledger around an abstract successful `JobProtocol` effect. It does
|
|
|
|
|
not redefine job phases or attempt-authority transitions.
|
|
|
|
|
|
2026-07-13 14:59:50 +09:00
|
|
|
## 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.
|
|
|
|
|
|
2026-07-13 17:23:32 +09:00
|
|
|
Worker failure outcomes accept only attempt-reportable runtime classes and do
|
|
|
|
|
not accept a caller-supplied retryable flag. The service derives the durable
|
|
|
|
|
disposition from the class mapping in the architecture. Validation,
|
|
|
|
|
cancellation, and lost-attempt projection use their dedicated boundaries.
|
|
|
|
|
|
2026-07-13 14:59:50 +09:00
|
|
|
## 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.
|