Guides

HTTP API

For a web app or desktop frontend, run oceanln-httpd instead of shelling out to the CLI. It's a thin loopback HTTP transport over the same oceanln-common core, so a UI can drive payout / offer / init over 127.0.0.1.

Run the server

oceanln-httpd --seed-file ./seed.txt --allow-origin http://localhost:5173
# oceanln-httpd listening on http://127.0.0.1:7762
# bearer token: <64 hex chars>      # printed once unless you pass --token

The seed never crosses the HTTP boundary: the server reads the 24 words from --seed-file per request, signs in-process, and never echoes them back. Seed generation stays a human-witnessed CLI operation and is deliberately not exposed over HTTP.

Security model

Because a browser is a supported client, the loopback port is guarded:

  • binds a loopback address only (refuses a routable --bind);
  • requires Authorization: Bearer <token> on every endpoint except /health (token auto-generated and printed, or set with --token);
  • enforces an Origin allowlist (--allow-origin, repeatable) — blocks cross-origin browser calls and DNS-rebinding;
  • validates the Host header is loopback;
  • the Lexe sidecar URL/credentials are server-side config (--sidecar-url / --sidecar-credentials), never taken from a request body (no SSRF).
oceanln-httpd --seed-file ./seed.txt \
  --sidecar-url http://127.0.0.1:5393 --sidecar-credentials <token>

Endpoints

All JSON; all but /health need the bearer token.

Method + pathBodyReturns
GET /health{"status":"ok"}
POST /generate{mnemonic, mining_address}
POST /import{mnemonic, force?}{mining_address}
POST /payout{message, offer?, description?, min_amount?, path?}{address, offer, message, signature}
POST /offer{description?, min_amount?}{offer}
POST /init{path?}{mining_address, provisioned}
shield

/generate and /import are the deliberate exceptions to "the seed never crosses the wire": /generate creates a fresh phrase, persists it, and reveals it exactly once so the user can back it up; /import accepts an existing phrase. Both refuse with 409 if a seed file already exists. Replacing a wallet must go through /import with {"force":true}.

Example: sign for an existing offer

/payout mirrors the CLI: pass offer to sign for an existing offer fully offline (it must be embedded in message), or omit it to have the configured sidecar create one first.

curl -s http://127.0.0.1:7762/payout \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"message":"<exact OCEAN message embedding the offer>","offer":"lno1..."}'