TESTED Microprogettazione v1.1 — allineata al codice al 4 maggio 2026. HTTP API Phase 1 (ADR 0078) realizzata e verificata live. 12/12 test verde via AioHTTPTestCase. Implementazione: /opt/myclaw/runtime/metnos_http_server.py + http_auth.py + http_render.py + http_routes_agent.py + http_routes_admin.py + 8 template Jinja2.
Stato nella sequenza: under approvalapprovedtestedimplemented. Pagina canonica creata il 4/5/2026 (ADR 0085). Affianca, non sostituisce, channel (Telegram resta canale primario per dialogo). Vedi anche pairing (multi-user via /start).
← Indice documentazione Microprogettazione › http_api

Metnos

http_api — server amministrativo + canale agent uniforme
Microprogettazione v1.1 — stato TESTED (4 maggio 2026)
Pubblico: chi vuole capire come si parla a Metnos via HTTP/browser/curl.

Lettura: 12 minuti.

Indice

  1. Cosa fa: secondo server HTTP
  2. Endpoint Phase 1
  3. Autenticazione e ruoli
  4. Content negotiation + ETag
  5. SSE su /agent/turn
  6. Dashboard /admin (htmx + uPlot)
  7. Rapporto con channel (Telegram) e pairing
  8. Limiti e rinvii a Phase 2

1. Cosa fa: secondo server HTTP

La HTTP API è il secondo server di Metnos: in ascolto su porta 8770, separata dal server di pairing dei device remoti (porta 8765). Espone tre cose: una dashboard amministrativa in HTML per il host, un canale agent uniforme (POST /agent/turn) per chi preferisce HTTP/SSE al posto di Telegram, una discovery del nodo (/.well-known/metnos.json) per i client.

La scelta di un secondo server (anziché estendere il pairing server o passare tutto da Telegram) e' motivata da tre vincoli: (a) l'admin UI deve essere a portata di browser senza che l'host debba aprire Telegram per gestire utenti, scheduler, proposte introvertiva; (b) il canale agent HTTP serve come fondamento per il client Rust e per integrazioni a venire; (c) il pairing server e' minimale per design (porta 8765, solo register-device) e non va appesantito.

Bind di default: 127.0.0.1:8770. Per esporre alla LAN, modificare il bind nella config (Phase 2 prevede TLS self-signed pin-by-fingerprint, riusando il materiale di pairing).

2. Endpoint Phase 1

MetodoPathRuoloScopo
GET/agent/healthanonymousLiveness + uptime + version.
GET/.well-known/metnos.jsonanonymousDiscovery: name, channels, capabilities, fingerprint admin key.
POST/agent/turnuser/adminEsecuzione run_turn. SSE se Accept: text/event-stream, altrimenti JSON.
GET/agent/devices/meuserInfo del device chiamante (id, paired_at, autonomy).
POST/agent/registeranonymousRegistra un device (pairing wrapper).
GET/adminadminDashboard root (HTML).
GET/admin/proposalsadminTabella proposte introvertiva (filtro kind).
POST/admin/proposals/{sig_key}/{approve|reject|defer}adminAzione su singola proposta.
GET/admin/executorsadminCatalog con lifecycle.
GET/admin/executors/statsadminCounts + daily events per uPlot.
GET/admin/runsadminScheduler runs (ultimi N).
GET/admin/safetyadminSafety signatures (ADR 0071).
GET/admin/turnsadminUltimi N turn (jsonl).
GET/admin/usersadminTabella utenti (host + guest).
POST/admin/usersadminCrea user da form (default owner = host).
GET/admin/users/{id}adminDettaglio utente.
POST/admin/users/{id}/deleteadminElimina (cascade su user_channels).
POST/admin/users/{id}/autonomyadminCambia autonomy_level.
POST/admin/users/{id}/channels/{channel}/pairadminIssue pairing token + istruzioni.
POST/admin/users/{id}/channels/{channel}/removeadminScollega canale.

Le rotte di collezione (/admin/proposals, /admin/executors, /admin/runs, /admin/safety, /admin/turns, /admin/users) sono negotiation-driven (vedi cap. 4).

3. Autenticazione e ruoli

Tre ruoli, in ordine crescente di privilegio: anonymous < user < admin.

Admin key

File ~/.config/metnos/admin.key (mode 0600), 256-bit hex generato con secrets.token_hex(32) al primo boot del server. Nei log compare solo il fingerprint sha256 (primi 16 hex). La admin key e' anche la master della cifratura credenziali (vedi cap. 7 e ADR 0082).

Login web: POST /admin/login con la admin key in chiaro; risposta set-cookie con TTL 7 giorni (HttpOnly, SameSite=Strict). Da CLI/curl: header Authorization: Bearer <admin.key>.

Bearer token device

Il middleware confronta il Bearer con la public_key_b64 di ogni device pairato (tabella devices di pairing). Match → ruolo user.

LAN trusted + whitelist anonymous

La distinzione «LAN trusted» e' figlia del modello self-hosted: sulla rete di casa il rischio e' contenuto, e l'host vuole curl/htmx senza header. La policy degrada appena il bind si apre alla WAN (Phase 2 con TLS).

4. Content negotiation + ETag

Le rotte di collezione esaminano Accept: text/html → fragment Jinja2 (htmx-friendly); altrimenti JSON. In entrambi i casi viene calcolato un ETag (sha256 del payload, primi 16 hex). Se il chiamante ripresenta If-None-Match con lo stesso valore, la risposta e' 304 Not Modified senza body. Smoke test live verificato 4/5/2026: round-trip ETag/304 OK.

5. SSE su /agent/turn

Quando il client manda Accept: text/event-stream, POST /agent/turn apre uno stream e installa un _SSEProgress (implementa l'interfaccia runtime.progress.Progress) che il run_turn invoca a ogni step. Eventi emessi:

EventoQuandoPayload
thinkingApertura stream + ogni LLM call in corso{"phase": "planner|intent|vaglio", "step": N}
progressNote utente (notify lunghe operazioni){"text": "..."}
tool_callPhase 2 — non emesso oggi{"name", "args"}
tool_resultPhase 2 — non emesso oggi{"name", "ok", "preview"}
finalChiusura stream con la risposta finale{"final_answer", "n_steps", ...}
errorErrore catturato dal turno{"error_code", "message"}

Il run_turn gira in un thread executor (sync) mentre la coroutine main pompa gli eventi sul loop tramite asyncio.run_coroutine_threadsafe.

Limite Phase 1. tool_call e tool_result non sono emessi: il Progress attuale non ha ancora questi callback. Phase 2 estende l'interfaccia.

6. Dashboard /admin (htmx + uPlot)

Stack frontend deliberatamente minimo: htmx + Jinja2 + uPlot via CDN, niente build step, niente framework CSS. Template compatti (<50 righe ciascuno) sotto runtime/templates/. Stile system-ui pulito, palette allineata al resto della doc canonica (navy / sage / bronze).

Pagine principali (rendering Jinja2 + frammenti htmx):

7. Rapporto con channel (Telegram) e pairing

Telegram resta canale primario per il dialogo conversazionale (long-poll, basso costo, coerenza UX su mobile). L'HTTP API e' canale alternativo per:

  1. Host browser: dashboard /admin per amministrazione (utenti, scheduler, proposte introvertiva, safety).
  2. Client Rust (fase 7): turn remoto da PC Windows/Linux pairato, via POST /agent/turn con SSE.
  3. curl/script: automazioni casalinghe veloci (curl -H "Authorization: Bearer ..." http://localhost:8770/agent/turn).

Il run_turn invocato via HTTP riceve channel="http"; non viene registrato un Channel formale nel channels.daemon (modello poll-based incompatibile con request/response). Per il vaglio e per policy l'effetto e' equivalente: gli step passano dagli stessi gate, l'audit conserva channel="http" nel turn log.

Multi-user (ADR 0083): l'HTTP API e' il punto di amministrazione della tabella users.db. Pairing dei guest via POST /admin/users/{id}/channels/telegram/pair: l'admin riceve un token short-lived, lo passa al guest, il guest manda /start <token> al bot. Il flusso e' descritto in pairing cap. 6.

Credenziali (ADR 0082): la admin key del server HTTP e' anche la master della cifratura simmetrica per ~/.config/metnos/credentials/<domain>.json.age. Chi controlla ~/.config/metnos/admin.key (mode 0600) puo' leggere le credenziali. Backup offline raccomandato.

8. Limiti e rinvii a Phase 2

Smoke live verificato 4/5/2026. GET /agent/health → 200 {ok:true, version:"1.1", uptime_s:...}. GET /.well-known/metnos.json → 200 con fingerprint admin key. GET /admin (no auth, da loopback) → 403. GET /admin (Bearer admin key) → 200 HTML dashboard. If-None-Match round-trip → 304.

Riferimenti codice: /opt/myclaw/runtime/metnos_http_server.py (entrypoint, factory, lock), http_auth.py (admin key + middleware), http_render.py (Accept negotiation + ETag), http_routes_agent.py (SSE, well-known, devices/me), http_routes_admin.py (collezioni read-only + actions), runtime/templates/ (8+ file Jinja2). Test: runtime/tests/test_http_server.py (12 test). ADR di riferimento: 0078 (HTTP API Phase 1), 0082 (credenziali), 0083 (multi-user), 0085 (allineamento doc 4/5/2026).