xynthis serve) wraps the agent loop and the brain on http://127.0.0.1:3939; this is what the macOS app talks to. The model server (xynthis-llm-serve) exposes the on-device model on http://127.0.0.1:8080 behind a standard OpenAI-compatible API. Both bind to loopback by default and have no authentication; do not expose them beyond the machine.
The daemon (127.0.0.1:3939)
Start it withxynthis serve [--port 3939]. Error handling follows one convention: when the brain is unreachable, handlers return HTTP 200 with an error envelope ({"op": "error", "message": "brain daemon offline"} or {"ok": false, "error": "..."} depending on the route), so clients poll one shape.
POST /api/chat
Runs one agent turn. Blocks until the turn finishes.message is required. sessionId is a stable per-conversation id; the daemon maps each one onto its own on-disk session so chat threads keep separate histories. Ids are restricted to [A-Za-z0-9_-], 1-128 characters; anything else is ignored and the daemon stays on its current session. tier ("free" / "pro" / "team") sets the memory-recall window for the turn: an explicit "free" caps recall to the last 7 days; "pro", "team", and absent mean unlimited. cwd binds the agent’s file tools and bash to a project root for the turn. A request with fleet_mode: true and tier: "free" is rejected with HTTP 402: multi-agent execution is Pro/Team only.
Response:
iterations is how many LLM round-trips the agent ran. POST /api/chat/stop cancels an in-flight turn.
GET /api/status
Returns the brain’s status JSON: uptime, perception counters, last dream, working-memory size, knowledge-graph size. Responses are cached for 1.5 seconds server-side, sized for the app’s 3-second poll; offline errors are never cached, so recovery shows immediately.GET /api/events
A Server-Sent Events stream of agent activity. Each event is a JSON object tagged with anevent field in snake_case: turn_started, llm_call_started, llm_call_completed, tool_called, tool_result, tool_denied, memory_recalled, memory_perceived, recall_judged, turn_completed, and others. Every event carries a ts unix timestamp. A consumer that falls behind receives {"event": "lagged", "dropped": N} and the stream continues; the daemon never drops a slow client. Streams end when the daemon shuts down.
GET /api/recall
tier=free caps results to the 7-day window, same as chat.
POST /api/remember
kind defaults to "note".
GET /api/sessions
Lists saved chat sessions, newest first. Sessions with zero messages are omitted.GET /api/session/transcript
{"id": "...", "title": "...", "messages": [{"role": "user", "text": "..."}]}. Internal scaffolding (injected memory context, working-memory blocks, tool-result turns) is stripped, so the messages are exactly what a chat UI should render.
GET /api/llm/list
Every provider configured inconfig.toml, with availability and the active marker. Powers the app’s model picker.
available comes from the credential scanner for remote providers; for local providers it is a live TCP probe of the configured port, so a configured-but-down local server shows as unavailable. Providers are ordered active first, then available, then alphabetical.
POST /api/llm/set
[models] active in config.toml, and saves. model is optional; it falls back to the provider’s default_model. Returns {"ok": true, "active": "ollama/llama3.3", "active_provider": "...", "active_model": "...", "active_base_url": "..."} or {"ok": false, "error": "..."}.
GET /api/extensions
Returns a snapshot of the loaded external MCP/webhook extensions: the same payload the agent’s ownextension_list tool sees.
Other routes
The daemon exposes further routes the app consumes: corpus management (/api/corpus, /api/corpus/add|remove|watch|update), permissions (/api/permissions, /api/permissions/set|preset), lessons, semantic search (POST /api/search/semantic), cortex, knowledge graph (/api/kg/graph), auth scanning (/api/auth/scan|apply), and voice transcription. They follow the same envelope conventions; the routes above are the stable integration surface.
The model server (127.0.0.1:8080)
xynthis-llm-serve exposes the on-device model over the OpenAI chat-completions standard, so any OpenAI-compatible client works against it unchanged. Port and bind address come from XYNTHIS_LLM_PORT (default 8080) and XYNTHIS_LLM_BIND (default 127.0.0.1). Only bind beyond loopback behind a tunnel or VPN.
GET /health
Returnsok. Liveness probe.
GET /status
serving is "student" once a fine-tuned LoRA adapter is attached, otherwise "teacher_backbone". jspace reports whether workspace capture is on for this process.
GET /v1/models
Advertises the model id (xynthis-local by default, override with XYNTHIS_LLM_MODEL_ID). Also reachable as /models; the /v1 prefix is optional on every route.
POST /v1/chat/completions
Standard OpenAI request body:model, messages (array of {role, content}), temperature, top_p, max_tokens, stream. One extension field:
session_id(string, optional): treat successive requests with the same id as one conversation. The server keeps the KV cache per session and reuses it when the new prompt extends the previous turn, which skips re-processing the shared prefix. Idle sessions are evicted after 30 minutes.
"stream": true the response is SSE: one chunk per token delta, then a finish chunk and data: [DONE].
The xynthis_jspace field
When the server runs withXYNTHIS_JSPACE=1, every chat response carries an additive xynthis_jspace block: the reply’s dominant concepts, read out from the model’s own activations while it generated: