Files
odysseus/companion

Companion bridge

A thin, additive layer so a LAN client (e.g. a phone) can discover what an Odysseus server offers and pair to it, without duplicating any LLM logic.

Method Path Auth Purpose
GET /api/companion/ping session or token cheap, auth-validated health check
GET /api/companion/info session or token server identity + capability flags
GET /api/companion/models session or token the caller's own model endpoints
GET /api/companion/pair admin cookie pairing page (a form; never mints)
POST /api/companion/pair admin cookie mint a one-time pairing token (?format=json for an in-app screen)

/models scopes to the caller's real owner plus legacy null-owner shared rows (same rule as owner_filter) and never returns API-key material.

Pairing CSRF posture

Minting happens only on POST. The session cookie is SameSite=Lax (routes/auth_routes.py), so a browser will not send it on a cross-site POST — the same protection POST /api/tokens relies on. A GET would be unsafe (Lax cookies ride top-level GET navigations), so GET /pair only renders a form. Minting invalidates the auth middleware's token cache, so a freshly minted token works on the next request without a restart.

The pairing/scoping rules live in small, tested units (token_owner, owner_can_see, mint_pairing_token, pairing.*) — see tests/test_companion_readonly.py and tests/test_companion_pairing.py.