Mosaic exposes a JSON REST surface plus a single WebSocket fan-out at /api/ws. Every endpoint the desktop SPA calls is reachable from the API; the SPA itself is a client of this surface, with a transport shim that swaps Wails IPC for fetch + WS when running in browser mode.

This page covers the cross-cutting concerns. For the per-endpoint reference, see REST Endpoints and WebSocket.

Base URL

http://127.0.0.1:<port>/api        # loopback-only mode (default)
https://<host>:<port>/api          # bind-all mode (LAN/external)

<port> defaults to 8080 and is configurable via WebConfigDTO.port. When the remote interface is enabled in loopback-only mode the listener binds 127.0.0.1 and speaks plain HTTP — no certificate is generated or used. Flipping “Bind to all” on makes the listener bind 0.0.0.0 (or [::]:<port>) over HTTPS with a self-signed cert generated at <DataDir>/web-tls/.

The self-signed cert covers localhost, 127.0.0.1, and ::1 only. Expect a browser warning on first connect, and pass -k (or pin the cert) for curl-driven scripts.

Transport

Surface Transport
REST application/json over HTTP (loopback) or HTTPS (bind-all)
WebSocket ws:///wss://<host>:<port>/api/ws matching the listener
File upload multipart/form-data (one route — POST /api/torrents/file)

Request bodies are JSON unless noted. Response bodies are JSON; errors share an envelope (see below).

Authentication

Two methods, accepted on every gated route:

  1. Session cookie — set on POST /api/login, sent automatically by the browser. Name: mosaic_session. HttpOnly, SameSite=Strict, Secure when behind HTTPS.
  2. Bearer API keyAuthorization: Bearer <key>. (The legacy ?key=<key> URL form has been removed for security; browser WebSocket clients rely on the session cookie instead.)

State-changing cookie-authed requests must satisfy the Origin/Referer CSRF guard: at least one of Origin or Referer must be present, and the host portion of whichever is present must equal Host. Bearer-keyed requests skip this check. See Authentication for the full flow.

Error envelope

Every error response carries the same shape — JSON regardless of route, no exceptions:

{ "error": "<message>" }
Status Meaning
200 Success.
400 Bad request — missing/invalid body, invalid params, validation failure.
401 Unauthorized — no valid session cookie and no valid bearer token.
403 Forbidden — Origin/Referer didn’t match Host on a cookie-authed state-changing request.
429 Too Many Requests — login rate limiter tripped. Retry-After: 12 header is set.
500 Internal error.
502 Upstream failure — e.g. blocklist URL didn’t return.

A success response is either an explicit shape (DTO or list of DTOs), or a generic ack: {"ok": true} for fire-and-forget commands, {"id": "<value>"} for create endpoints.

Versioning

Mosaic does not currently version its API surface in the URL. The DTOs are documented per release; backwards-incompatible field renames will bump the minor version and call out the change in release notes. The build’s version string is reachable at GET /api/version ({"version": "vX.Y.Z"}).

Quick check

# Replace with your own
export MOSAIC="https://localhost:8080"
export MOSAIC_API_KEY="<paste-from-Settings-WebInterface>"

curl -k "$MOSAIC/api/version" -H "Authorization: Bearer $MOSAIC_API_KEY"
# → {"version":"v1.5.0"}

curl -k "$MOSAIC/api/torrents" -H "Authorization: Bearer $MOSAIC_API_KEY"
# → [...torrent rows...]

If you get {"error":"unauthorized"}, double-check the API key and that the bearer header isn’t being stripped by an intermediate proxy.