| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .. | ||
| events.schema.json | ||
| openapi.yaml | ||
| README.md | ||
Remora API implementation contract
These files are mutable implementation artifacts. They describe the current
HTTP operations and WebSocket frames but are not authoritative over
Remora.tla or
architecture.md. Conflicts are corrected here.
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. Each operation identity retains a request fingerprint. An
exact replay has the original effect; a changed worker, fence, or payload
returns precondition_failed without mutation. First observation is accepted
only when the corresponding JobProtocol action is enabled. Later eligibility
changes do not alter a stored response, while a fresh operation is evaluated
against current eligibility.
Renewal follows RenewLease: it may extend an existing live coordination lease
after durable authority changes. It never creates or restores a claim. Progress
and outcome operations require current fenced authority and return
precondition_failed for stale or non-authoritative tuples.
A successful claim returns the attempt authority and the attempt's
ExecutionEnvelope. The worker verifies that their job, attempt, fence, and
worker incarnation agree before source or target I/O. Opaque resolved-source
and checkpoint references identify durable streamable state; they are not
reconstructed from coordination data.
RemoraApiMutation models this durable
operation ledger around an abstract successful JobProtocol effect. It does
not redefine job phases or attempt-authority transitions. Its Rejected
response abstracts the endpoint-specific idempotency_conflict and
precondition_failed codes enforced by the executable contract.
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.
A deletion whose exact remaining work cannot progress because target access or
configuration is unavailable enters blocked with a redacted problem. It
retains the deletion obligation and can return to active processing when the
dependency is restored. Owner access remains authorized through the concrete
target retained in the tombstone.
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.
WebSocket replay
An HTTP job or list snapshot carries event_cursor at its consistency
watermark. The client connects to /v1/events with that cursor; there is no
subscription frame. Server frames are ordered by increasing sequence for a
connection and carry an opaque next cursor. Delivery is at least once, and
clients deduplicate event IDs.
job_changed is an invalidation with an attempt-aware display hint, not a
resource replacement or patch. A client compares job_revision with the last
HTTP snapshot revision and fetches a new authorized snapshot when it is newer.
It advances its applied revision only from that snapshot. Matching progress may
be displayed provisionally while the fetch is pending.
deletion_changed is likewise an invalidation. Its state is a display hint;
the deletion resource remains the authoritative HTTP projection.
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.