← Documentation index Architecture guide › HTTP API

Metnos

Web chat and HTTP API
One boundary for conversation, administration, and resumable state.

metnos_http_server serves the web chat, turn API, and Settings area. A new guided installation defaults to 0.0.0.0:8770 so the UI is reachable from the same private LAN; phase 4 can select loopback-only access or a different port. The installer prints the exact usable URLs and saves them. The remote-executor protocol on 8765 and browser service on 8771 are separate components with their own authority and routes.

Connecting after installation

At the end of phase 6, the installer shows one complete, one-use administrator URL for the server and, when LAN access is enabled, one for every detected private IPv4 address. It saves the same URLs in ~/.local/share/metnos/install_summary.md.

You do not need to derive an address or replace a placeholder: use one of the printed URLs exactly as shown, including its scheme, address, port, and temporary token. If the token has expired, run ./.venv/bin/python -m install --force-phase 6 again or sign in at /admin/login. Do not forward the port from a router or expose the plain-HTTP listener directly to the Internet.

Opening the Web UI from another browser is only interface access and does not require installing or pairing a remote executor. Pairing is needed only when the remote computer must also perform operations on its own local data.

Contents

  1. Connecting after installation
  2. A natural-language example
  3. Server composition
  4. Main routes
  5. Identity and authorisation
  6. Sessions and per-user isolation
  7. Direct and resumable turns
  8. HTML, JSON, and caching
  9. Settings and localisation
  10. Network and capacity boundaries
  11. Verification and canonical sources

1. A natural-language example

Ask Metnos with a request such as:

Show me where I can inspect recent turns and change the model configuration.

The answer should begin with the route visible in the chat: Settings > Activity > Turns and Settings > System > Models. If the question came from Telegram, the answer must state that Settings opens in the web chat. Only a person asking for a technical integration then needs the HTTP paths /admin/turns and /admin/virt.

2. Server composition

The make_app factory creates one aiohttp application with a 50 MiB request-body limit and mounts three route registries:

ModuleResponsibility
http_routes_agent.pyChat, discovery, turns, sessions, dialogs, attachments, web pairing, and OAuth callback.
http_routes_admin.pySettings, users, models, services, devices, scheduler, Safety, and executor evolution.
http_routes_stack.pyCombined readiness for the server, catalog, and browser service.

The same middleware order applies to every route: the first layer classifies the caller; the second places that user's language in the request context. The language context is copied into the turn pool as well, so concurrent requests do not share a global language variable.

2.1 Adjacent services

3. Main routes

3.1 Entry and discovery

Method and pathFunctionAccess
GET /Web chat; an anonymous caller is redirected to administrator login.User or administrator; initial anonymous entry is admitted for the redirect.
GET /agent/healthLiveness, API version, product version, and uptime.Anonymous.
GET /.well-known/metnos.jsonNode descriptor and pairing URL; no administrative key or fingerprint is disclosed.Anonymous.
POST /agent/registerConsumes a remote executor's one-time token.Anonymous with a valid token.
GET /pair/{token}Consumes a web pairing token and sets the paired-device cookie.Anonymous with a valid token.
GET /oauth/callbackCompletes an OAuth flow using short-lived state issued by Metnos.Public for redirect purposes; state is the boundary.

The PWA manifest, service worker, and files below /static/ are available without authentication. Photographs below /agent/photos/ instead require a signed, expiring URL: the URL is itself a capability and must be treated as confidential.

3.2 Turns, sessions, and dialogs

GroupPathsContract
Direct turnPOST /agent/turnJSON or an SSE stream on the same connection.
Resumable turnPOST /agent/turn/submit; GET /agent/turns/{id}/stream; GET /agent/turns/{id}Immediate acceptance, reconnectable events, and polling fallback.
History and judgementGET /agent/turns/recent; POST /agent/turns/{id}/feedback; POST /agent/turns/{id}/retryConversation history, explicit feedback, and retry.
Writer session/agent/session/register, takeover, ping, revoke, eventsOne writing device per user-channel pair and explicit transfer.
Dialogs/agent/dialog/{id}/form, submit, cancel, preview, contextInput collection with ownership and, when needed, a short-lived delegable capability.
Attachments/agent/gallery/{turn_id}, /agent/photos/{turn_id}/{idx}, /agent/photos/webOwner-bound gallery; signed local-file URLs; web proxy with SSRF defences and size limits.

3.3 Administration

Every route below /admin, except the login entry, requires the administrator role. The main areas are:

The code-level route registries remain authoritative. This grouping explains the surface without freezing a route count that would become stale.

4. Identity and authorisation

Middleware assigns one of three roles: anonymous, user, or admin:

  1. a Bearer equal to the administrator key grants admin;
  2. another Bearer is valid only if it matches a registered device;
  3. without a Bearer, a valid administrator or user cookie grants the corresponding role; the user cookie is also checked against the pairing registry, which is the revocation authority;
  4. when no credential was presented, selected local addresses may receive user as an installation-compatibility path;
  5. otherwise the caller remains anonymous.

An invalid Bearer never falls through to local-network trust. Standard forwarding headers such as X-Forwarded-For are considered only when the TCP peer belongs to METNOS_TRUSTED_PROXIES; an ordinary client cannot declare itself local through a header.

The administrator key lives at PATH_USER_CONFIG/admin.key, is created with mode 0600, and also derives signatures and encryption keys through separate domains. The administrator cookie lasts seven days, is HttpOnly, uses Secure when the external request is HTTPS, and is SameSite=Strict; the user cookie lasts ninety days and is HttpOnly, Secure, and SameSite=Lax.

5. Sessions and per-user isolation

The active session is keyed by (user_id, channel). Hosts and guests can therefore use the chat concurrently without sharing a lease, conversation, or browser-local memory. The client also separates tokens, command buffers, and history through an opaque scope derived from the user.

When a second device finds an active session, the server issues a one-time token and leaves three choices to the interface:

Transfer rechecks both user and old writer atomically. Recent history requires a conversation belonging to the user. Turn status, stream, gallery, feedback, and retry also verify the turn owner: knowledge of a turn_id does not grant access. Older records without owner_user_id use only deterministic conversation or actor bindings; an identity-store failure is denied rather than converted into permission.

6. Direct and resumable turns

6.1 Direct request

POST /agent/turn accepts JSON or multipart data containing a request and reference images. With Accept: text/event-stream it sends thinking, progress, and tool_call events followed by final or error; periodic comments keep the connection alive. Without SSE it returns one JSON document.

6.2 Execution independent of the connection

POST /agent/turn/submit first reserves pool capacity and then returns 202 with a turn_id and stream_url. The turn continues if the page refreshes or the network drops. The stream accepts Last-Event-ID and replays later events; GET /agent/turns/{id} provides a polling fallback.

In-memory events remain for a bounded interval after completion. Once they are gone, the status route searches the persisted daily record. Persistence does not replace the ownership check.

6.3 Bounded capacity

Blocking turns pass through a dedicated pool with a global limit, a finite queue, and a per-principal limit. If capacity cannot be reserved within the admission window, the server returns 503 turn_capacity_exhausted with Retry-After: 1. A disconnect does not release capacity prematurely while the worker thread is still running.

7. HTML, JSON, and caching

Administration collections that use negotiate_collection return HTML when Accept contains text/html, and JSON otherwise. The body receives a SHA-256-derived ETag; an equal If-None-Match produces 304. This does not apply to every route: some are HTML-only, JSON-only, or SSE.

The chat uses no-cache, no-store so the client receives the current contract. Negotiated administration responses have a short private cache; model configuration and services use no-store. Signed attachment URLs have their own lifetime and must not be confused with a user session.

8. Settings and localisation

Settings uses Jinja2 templates, the runtime/ui_surfaces.py registry for navigation and descriptions, htmx for selected updates, and uPlot only in the executor-statistics view. There is no front-end build step.

Every visible string must come from the i18n catalog or already-localised data. Language is an individual user preference held in a request-local ContextVar; a new language requires catalog entries, a detection lexicon, and corresponding documentation rather than phrase-specific conditions in templates.

Settings is the interactive administration interface. The HTML report generated by runtime.observability is a separate artifact described in operational observability.

9. Network and capacity boundaries

10. Verification and canonical sources

curl -fsS http://127.0.0.1:8770/agent/health
curl -fsS http://127.0.0.1:8770/.well-known/metnos.json
curl -fsS -H "Authorization: Bearer <administrator-key>" \
  http://127.0.0.1:8770/agent/stack/health

The first probe proves liveness only. The third is suitable evidence before a restart or a full-stack test. Suites below tests/runtime/http/ cover authentication, sessions, turn isolation, SSE, caching, uploads, rendering, and administration pages; this guide does not freeze a test count.