Install and start
--http starts the front in the same process, as a client of the local API under the application ID offline-protocol-http-front. offline-protocol-http-front runs it as a separate process instead. Run one front per service: two fronts on one host both receive every request and call the callback twice. GET /health on the front returns its name, version, connection state and local_address, the host’s own off1... address.
Register a provider
A provider is any HTTP endpoint on the providing host. Register it with the front on that host:409 when another local application owns the name. The callback is an http or https URL with no query or fragment; the request’s path is appended to it.
Call a service on another device
<service>.<device>.<domain>. The device is the provider host’s off1... address, which is a DNS label as it stands, or an alias from the operator’s --http-aliases file ({"aliases": {"bob": "off1..."}}). An alias is never learned from the network. The domain defaults to offline.protocol.internal and can be changed with --http-domain. The front routes on the host name, so a resolver entry or a wildcard DNS rule that points the domain at the front removes the need for a manual Host header.
The providing front calls the callback with the method, the path, the carried headers and the body, plus three headers of its own:
A requester can ask for a shorter or longer deadline with
X-Offline-Protocol-Timeout: <seconds>.
Who can call
The front does not authorize. Any device whose message the engine decrypts can call every registered service, and withauto_key_exchange on that is any device running the SDK that can reach the host. The provider decides who it serves, for example by comparing X-Offline-Protocol-Sender with an allowlist of addresses it received out of band.
That header cannot be forged through the front, but it is unsigned on the callback request. Trust it only when nothing but the front can reach the callback, such as a callback on loopback in a network namespace the front alone shares. A callback on a shared container network or a published port receives whatever header any program sends.
Off loopback, the front requires a per-launch token: --http-token-file PATH writes a new 32-byte token at every start, and clients present it in X-Offline-Protocol-Token. On any binding the front refuses a request that carries an Origin header, so a web page cannot send a body or register a callback through it by DNS rebinding. A page can still make a blind GET by loading a resource, so a host where a browser runs should use a token on loopback too. The SDK threat model records this as R23, “The HTTP front trusts every process that can reach it”.
Limits
Only listed headers cross:
accept, accept-language, authorization, cache-control, content-language, content-type, etag, if-match, if-none-match, last-modified, location, and x- names other than x-offline-protocol-*, x-forwarded-*, x-real-ip, x-original-url and x-rewrite-url. A path with a . or .. segment is refused.
The body limit does not fit every carrier. A sealed envelope over about 71 KB does not cross Bluetooth LE at the 185-byte MTU floor, nor one over 64 KiB a Nostr relay; a request whose only route is such a carrier ends as deadline_exceeded.
A 504 does not mean the request did not run. The requester stops waiting, but the message is not withdrawn: the engine keeps retrying it, and a provider that receives it within 60 seconds past its deadline still calls the callback. A request that changes state should carry an idempotency key of the application’s own in an x- header, because X-Offline-Protocol-Request-Id is new on every retry.
Errors
An error response carries its token in
X-Offline-Protocol-Error.
Run it in a container
The SDK repository’sbindings/python/docker builds one service per host on python:3.12-slim, with the lan and http extras installed from PyPI and the front on 127.0.0.1:8080. No image is published to a registry; build it from that directory. It is configured by environment variables (OP_LISTEN, OP_PEERS, OP_LAN, OP_CONFIG, OP_HTTP, OP_HTTP_TOKEN_FILE, OP_HTTP_ALIASES, OP_RELAY, OP_GATEWAY), and its README lists what the host must grant:
examples/http-front runs two boxes on one machine with Docker Compose, a provider with no SDK import on box B and a client on box A, each sharing its box’s network namespace:
--allow off1... to serve only listed callers. When box B’s container is recreated, recreate the provider too: a container that joined the old network namespace stays in it.
Reference
- HTTP front chapter: the hostname, envelope, limits, errors and authorization.
- Local API guide, the HTTP front.
- Threat model, R23.

