Documentation / Operations

HTTP interface

Resource endpoints and authority.

ROM 0.1.0 · SOURCE CANDIDATE Markdown

rom-http binds registered Resource definitions through one generic protocol. The host constructs Http::new(runtime, resolver, limits) and runs http.serve(listener, stop_future). No Resource-specific controller or route is needed. AuthResolver is a fast, nonblocking host function from HTTP headers to Result<Actor>. Use a verified credential context. Never use a claimed subject header. Actor is deliberately not deserializable. The binding does not implement cryptographic credential verification or TLS.

All routes use POST with JSON. Clients can use a streaming fetch client for SSE; these POST streams are not the browser's GET-only EventSource API.

RouteRequestResponse
/discover{}Authorized Discovery catalog
/invokeInvocationProjectedView
/read{kind,id}ProjectedView
/query{kind,query} or legacy {kind,field,value}Array of projected views
/live{kind,query} or legacy {kind,field,value}SSE current snapshots, initial then changes
/journal/head{kind}Explicit current JournalCursor
/journal{kind,after}Bounded JournalBatch
/subscribe{kind,after}SSE ordered journal batches
/work/capabilities{}OperatorCapabilities
/work/listWorkQueryWorkPage
/work/read{handle}WorkView
/work/controlWorkControlRequestWorkControlResult

Operator routes use the authorized work recovery contract. Their policy defaults to denial and is independent of Resource discovery.

Example invocation:

json
{"kind":"tasks","id":"one","expected":1,"idempotency":"finish-1","operation":{"type":"action","input":{"name":"complete","input":null}}}

The discovery catalog requires explicit metadata grants, defaults to no visible Resources, and does not assert permission to read or mutate rows. Custom action inputs remain opaque. Hidden reference targets are not disclosed.

Operation tags are create, replace, patch, delete, and action. Create/replace carry a complete Resource value in input; delete has no input. Replacement is not a partial patch. Explicit PATCH distinguishes omission, null and removal. Custom action input is checked by its registered codec. Unknown request fields and duplicate JSON keys, including nested duplicates, fail before dispatch. All results use the current row and field projection policy. Predicate authority is checked even when no row matches.

Runtime::execute<R> and Runtime::invoke share the same mutation pipeline. The former exposes a complete typed Resource; a partial field grant requires invoke_projected. The HTTP binding always uses projected results. Request routing, idempotency, payload and host identity are counted before allocating the durable mutation identity. Same identity with different semantic input is identity_mismatch. A transport disconnect does not cancel accepted work.

Live state coalesces invalidations and recomputes an authorized bounded snapshot. It has no replay cursor. Journal facts are ordered and never coalesced. The journal cursor includes history generation, kind and global position. A batch can contain no visible facts and still advance over inaccessible or other-kind history. Cursor positions therefore reveal coarse history progression; they are not secret or authorization credentials. Raw storage identities and raw rows are never serialized through the journal endpoint.

Only after you process the batch, persist the returned journal cursor. The server holds no durable consumer acknowledgement. Wrong generation/kind, future cursor, and a cursor before the retention floor produce history_gap (410), including a missing cursor after the beginning has expired. Never treat this response as successful processing or silently restart from the retained tail.

If you consciously accept lost history, request /journal/head. Obtain an authorized snapshot. Subscribe from that head. Events after the head can already appear in the snapshot. Reconcile overlap by Resource revision. Retention can race this recovery and produce another explicit gap. This sequence rebuilds state; it cannot reconstruct lost business-event processing.

SSE data events contain JSON; error events contain one safe error category and terminate the stream. Keepalive comments carry no Resource information. Each stream owns a ROM subscription permit until drop. Polling also rechecks expiry without requiring a Resource write; the default poll interval is 100ms. Polling performs cheap lifecycle/expiry/local-revocation checks. It retains one pending read across ticks, without cancellation and restart of a slow query. Tune it to the host's revocation latency and capacity requirements.

Limits independently bounds body bytes, accepted concurrent bodies and body read time. Core limits bound accepted actions, I/O jobs, subscriptions and snapshot rows/bytes. Concurrency counts above Tokio's semaphore maximum return configuration errors before constructing a runtime or HTTP binding. Slow request bodies time out as overloaded (429), and oversized declared or chunked bodies are too_large (413). Other mappings are 403 denied, 404 missing/unregistered, 409 conflict or identity mismatch, 400 invalid/unsupported, 503 closed/not committed/outcome unknown, and 500 internal. Wire errors omit raw input, credentials, driver errors and source paths.

Use Http::serve for coordinated shutdown: intake closes, SSE handles terminate, and accepted ROM work drains. Hosts using router() directly must call Http::shutdown() and coordinate their own server shutdown. Native blocking business/storage work must terminate; graceful drain is not a hard kill deadline. Host connection/header limits, TLS, reverse proxy behavior, origin policy and external-provider interoperation remain deployment responsibilities. This MVP runs one ROM owner per storage instance; it does not coordinate cross-process live notifications or worker ownership.

An error or lost response after submission is not blanket proof that no commit occurred. In particular, revocation after commit and before disclosure can produce denied. The committed bundle remains. Keep the same idempotency identity for reconciliation. Never invent another identity solely because a result was unavailable. There is no unauthenticated receipt-status escape hatch.

This page follows the captured repository source.View the source