Reference for the HTTP API exposed by maylyn serve (and maylyn hub, which
mounts the same routes with inference off). The route table is registered in
NewServeMux in backend/serve.go and is the source of
truth; if this page and that file disagree, the code wins.
Version this page was checked against: v0.44.1.
Contents
Quick start
MAYLYN_SERVE_TOKEN=$(openssl rand -hex 32) maylyn serve
# maylyn serve listening on 127.0.0.1:8787
curl http://127.0.0.1:8787/health
curl -N http://127.0.0.1:8787/ask/stream \
-H "Authorization: Bearer $MAYLYN_SERVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt": "say hello in one short sentence", "tools": false}'
If MAYLYN_SERVE_TOKEN is unset, a random token is generated and printed on
startup.
| Variable |
Default |
Purpose |
MAYLYN_SERVE_ADDR |
127.0.0.1:8787 |
Listen address. Loopback by default; a warning is printed if you widen it |
MAYLYN_SERVE_TOKEN |
generated |
Static bearer token (admin/legacy credential) |
MAYLYN_SERVE_ALLOW_TOOLS |
false |
Let HTTP callers run approval-gated tools (shell_exec, write_file, ...) unattended |
MAYLYN_SERVE_WEBAPP |
config webapp_enabled |
Mount the bundled browser/phone UI. The env var overrides config in either direction |
MAYLYN_SERVE_AUTH_MODE |
kratos |
kratos (Ory Kratos sessions, plus the static token as a fallback) or local (static token only) |
MAYLYN_KRATOS_PUBLIC_URL |
http://127.0.0.1:4433, or <hub>/kratos when a hub is configured and serving Kratos |
Kratos public base URL |
MAYLYN_KRATOS_START_CMD |
empty |
Optional command to autostart a localhost Kratos |
MAYLYN_SERVE_ALLOW_TOOLS=true means a POST body can run a shell as the user
that owns the process. Do not combine it with a non-loopback address unless
that is exactly what you intend.
Conventions
- Base URL:
http://127.0.0.1:8787 unless MAYLYN_SERVE_ADDR says otherwise.
- Bodies: JSON (
Content-Type: application/json) unless noted. Request
bodies are capped at 4 MiB; an invalid or oversized body returns 400.
- Errors: standard HTTP status (
400, 401, 403, 404, 429, 5xx).
Most handlers answer with a plain-text body; the rate and concurrency
limiters answer 429 with {"error": "..."} and a Retry-After header. A
missing or bad credential returns 401 with
unauthorized: log in, or send Authorization: Bearer <token>.
- Path parameters are written
{like_this}.
- Per-person scoping: with a Kratos session, conversations,
artifacts, memories and agents resolve to that person's own store. The static
token resolves to the machine's shared store.
- Client hint: the optional
X-Maylyn-Client header (desktop or webapp)
tells the server which shell is calling and may change how a turn is shaped.
Authentication
Every route is in one of these classes. The tables below label each route with
the class that applies.
| Class |
Meaning |
| Public |
No credential. Safe-to-expose status, public text, or web UI assets |
| Bearer |
Authorization: Bearer <token> where the token is the static MAYLYN_SERVE_TOKEN. When Kratos is reachable (the default auth mode) a verified Kratos session, sent as a browser cookie, is also accepted |
Bearer or ?token= |
As Bearer, and additionally the static token may be passed as ?t= or ?token= for <img>, <a> and EventSource, which cannot set headers |
| Socket |
WebSocket upgrade; authenticated inside the handler |
| URL token |
The secret is part of the path (/hooks/{token}, /share/{token}, ...); no header needed |
| Signed |
Peer-to-peer mesh traffic authenticated by an ed25519 signature on the envelope |
Details:
- The static token is compared in constant time over SHA-256 digests.
- A verified Kratos session wins over the static token and scopes the request
to that person's store. Banned profiles get
403.
- With Kratos active, unverified accounts get
403 unless they are the machine
owner.
- The webhook route
POST /automation/webhook/{id} is Bearer-authenticated
and additionally reads an X-Maylyn-Webhook-Token header.
Streaming chat
Two routes run the agent loop and answer as Server-Sent Events
(Content-Type: text/event-stream).
POST /ask/stream
One-shot prompt. Bearer.
{ "prompt": "what changed in the repo?", "agent": "maylyn", "tools": true }
| Field |
Type |
Notes |
prompt |
string |
Required. Empty returns 400 prompt is required |
agent |
string |
Agent profile name; default agent if empty |
tools |
bool |
Enable tool calls for this turn |
POST /chat/stream
Multi-message chat. Bearer.
{
"messages": [{ "role": "user", "content": "hello" }],
"agent": "maylyn",
"tools": true
}
| Field |
Type |
Notes |
messages |
[{role, content}] |
Conversation so far; the last entry is the new user turn |
images |
array |
Images attached to the last user message |
agent |
string |
Agent profile name |
model |
string |
Model override |
conversation_id |
string |
Attach the turn to a saved conversation |
knowledge_base_id |
string |
Retrieve from this knowledge base |
skill |
string |
Pin one skill into the turn |
max_iterations |
int |
Tool-loop cap |
context_tokens |
int |
Context window override |
auto_approve |
bool |
Skip approval prompts (the server still refuses unattended tools unless MAYLYN_SERVE_ALLOW_TOOLS is set) |
tools |
bool |
Enable tool calls |
tool_overrides |
object |
Per-tool picker: { "tool_name": "auto" } never asks, "off" hides the tool for this turn |
concise |
bool |
Voice-originated turn; keep the reply short and speakable |
resume |
bool |
Resume an interrupted turn |
steer |
string |
Redirect a running generation |
thinking |
string |
Reasoning-effort hint |
mode |
string |
Turn mode |
mesh_pack |
string |
Route the turn to a Pack |
The full struct is chatStreamRequest in backend/serve.go.
Stream events
Event payloads are JSON. Names that the server emits include:
| Event |
Meaning |
meta |
Turn metadata (agent, model) sent first |
response_chunk |
A piece of the assistant reply |
response_retract |
Earlier streamed text was withdrawn |
thinking_chunk |
A piece of model reasoning |
tool_call |
The model called a tool (name, args) |
tool_result |
A tool finished (name, result) |
tool_denied |
A tool was refused (approval gate or unattended policy) |
status / notice |
Progress or advisory text |
timing |
Latency breakdown (setup, session, retrieval, pre-model) |
error |
The turn failed |
done |
The turn finished |
Other chat routes
| Route |
Auth |
Purpose |
GET /chat/ws |
Socket |
WebSocket variant of chat/stream used by the web client |
POST /chat/stop |
Bearer |
Stop the running turn. Prefer this over closing the stream |
GET /approvals |
Bearer |
List pending tool approvals |
POST /approvals |
Bearer |
Answer a pending approval. Only the session that raised the call may answer |
Endpoint reference
Routes are grouped by area. "Bearer" includes verified Kratos sessions.
Health and downloads
| Route |
Auth |
Purpose |
GET /health |
Public |
Liveness plus process identity (status, pid, build id, executable) |
GET /downloads/latest |
Public |
Latest release metadata (proxy used by the website) |
GET /downloads/asset/{name} |
Public |
Release asset download proxy |
Web UI shell (only when the web app is enabled)
All Public: GET / (shell), /styles.css, /app.js, /bootstrap.js,
/thinking_phrases.json, /manifest.webmanifest, /sw.js, /favicon.ico,
/icon-192.png, /icon-512.png, /icon-192-maskable.png,
/icon-512-maskable.png, /apple-touch-icon.png, /mascot.png, and the
prefixes /voice/canned/, /controllers/, /vendor/, /icons/achievements/,
/icons/weather/. When the web app is off these return 404.
Sign-in and accounts
| Route |
Auth |
Purpose |
GET /auth/config |
Public |
Which auth mode is active and where flows live |
GET /auth/kratos/login register recovery settings verification logout |
Public |
Redirects into the matching Kratos browser flow |
/kratos/ |
Public |
Reverse proxy to the Kratos public API (kratos mode) |
GET /access/required |
Public |
Legacy: whether an access key is needed |
POST /access/validate |
Public |
Legacy: validate an access key |
GET /me |
Bearer |
Current profile |
POST /me/claim-owner |
Bearer |
Claim machine ownership |
POST /me/name |
Bearer |
Update display name |
POST /me/bio |
Bearer |
Update bio |
GET /me/avatar POST /me/avatar DELETE /me/avatar |
Bearer |
Read, upload, or remove the avatar |
POST /keys/invite |
Bearer |
Create an invite |
GET /admin/people |
Bearer |
List accounts (owner) |
POST /admin/people/revoke |
Bearer |
Revoke an account |
POST /admin/people/ban |
Bearer |
Ban or unban an account |
POST /admin/invites/revoke |
Bearer |
Revoke an invite |
Agents, models and setup
| Route |
Auth |
Purpose |
GET /agents |
Bearer |
List agent profiles |
GET /models |
Bearer |
List available models |
GET /models/media PUT /models/media |
Bearer |
List every installed model, or set its image/video/audio input (Auto/On/Off) |
DELETE /models/installed |
Bearer (owner) |
Uninstall any installed model by its path from GET /models/media; refuses the running model |
GET /models/image |
Bearer |
Image models in the catalog (SDXL checkpoints and FLUX.1 schnell), with install state |
POST /models/image/install DELETE /models/image |
Bearer (owner) |
Install (a setup job; follow /setup/job/events) or remove an image model by name |
GET /setup/state |
Bearer |
First-run setup state |
POST /setup/model /setup/art /setup/music |
Bearer |
Start an install job |
GET /setup/job/events |
Bearer or ?token= |
SSE progress for the running setup job |
POST /setup/job/cancel |
Bearer |
Cancel the setup job |
POST /setup/complete |
Bearer |
Mark setup finished |
GET /capabilities |
Bearer |
Feature and plugin capability matrix |
POST /capabilities/plugin |
Bearer |
Enable or disable a plugin |
GET /diagnostics/control |
Bearer |
Control-plane diagnostics |
GET /vitals |
Bearer |
Machine vitals |
Skills and library
| Route |
Auth |
Purpose |
GET /skills |
Bearer |
List skills |
GET /skills/{name} |
Bearer |
Read one skill |
POST /skills/preview |
Bearer |
Preview a skill before installing |
POST /skills/install |
Bearer |
Install a skill |
DELETE /skills/{name} |
Bearer |
Remove a skill |
GET /library/models DELETE /library/models |
Bearer |
List or delete models installed from Hugging Face search |
GET /library/models/search |
Bearer |
Search for models |
POST /library/models/install |
Bearer |
Install a model |
GET /library/offline DELETE /library/offline |
Bearer |
List or delete offline packs |
GET /library/offline/catalog |
Bearer |
Offline pack catalog |
POST /library/offline/install |
Bearer |
Install an offline pack |
GET /library/mcp-directory |
Bearer |
Browse the MCP connector directory |
POST /library/mcp-install DELETE /library/mcp-install |
Bearer |
Add or remove an MCP server |
POST /library/mcp-check |
Bearer |
Test an MCP server |
Conversations, shares and attachments
| Route |
Auth |
Purpose |
GET /conversations |
Bearer |
List saved conversations |
GET /conversations/{id} |
Bearer |
Read one |
GET /conversations/{id}/artifacts |
Bearer |
Artifacts produced in a conversation |
POST /conversations |
Bearer |
Save a conversation |
DELETE /conversations/{id} |
Bearer |
Delete a conversation |
POST /wiki/consolidate |
Bearer |
Consolidate memory into the wiki |
POST /shares/oneoff |
Bearer |
Create a one-off read-only share link |
GET /shares |
Bearer |
List shares |
GET /shares/{id}/preview |
Bearer |
Preview a share |
DELETE /shares/{id} |
Bearer |
Revoke a share |
GET /share/{token} |
URL token |
Replay a shared conversation |
POST /attachments/parse |
Bearer |
Parse an uploaded attachment into text |
Knowledge bases and projects
| Route |
Auth |
Purpose |
GET /knowledge-bases POST /knowledge-bases |
Bearer |
List or create |
GET /knowledge-bases/{id} DELETE /knowledge-bases/{id} |
Bearer |
Inspect or delete |
GET /knowledge-bases/{id}/chunks |
Bearer |
Indexed chunks |
POST /knowledge-bases/{id}/sources DELETE /knowledge-bases/{id}/sources |
Bearer |
Add or remove a source |
GET /projects POST /projects |
Bearer |
List or create projects |
POST /projects/active |
Bearer |
Set the active project |
DELETE /projects/{name} |
Bearer |
Delete a project |
GET /projects/pending |
Bearer |
Project handed off from the CLI, awaiting a decision |
GET /projects/detect |
Bearer |
Detect a project in a directory |
POST /projects/link |
Bearer |
Link a directory as a project |
POST /projects/index |
Bearer |
Start indexing |
GET /projects/index/{id} |
Bearer |
Indexing status |
Files and artifacts
| Route |
Auth |
Purpose |
GET /files GET /files/ |
Bearer or ?token= |
File browser and file reads inside the sandbox |
GET /plugins/files/list GET /plugins/files/read |
Bearer |
List and read sandbox files |
POST /plugins/files/mkdir create rename delete upload |
Bearer |
Sandbox file operations |
GET /artifacts/ |
Bearer |
Serve a stored artifact file |
GET /plugins/artifacts/history |
Bearer |
Artifact revision history |
POST /plugins/artifacts/delete |
Bearer |
Delete an artifact |
POST /plugins/images/upload |
Bearer |
Upload an image |
POST /plugins/pixel/analyze |
Bearer |
Analyze pixel art |
POST /plugins/dice/roll |
Bearer |
Roll dice |
Studio, canvas and sound
| Route |
Auth |
Purpose |
GET /studio/assets POST /studio/assets |
Bearer |
List or create art-studio assets |
GET /studio/assets/{id} |
Bearer |
Read an asset |
POST /studio/assets/{id}/animation/op animation/export |
Bearer |
Animation edit and export |
POST /studio/assets/{id}/sound/op sound/render sound/export |
Bearer |
Sound edit, render, export |
GET /studio/assets/{id}/sound/graph |
Bearer |
Sound node graph |
GET /canvas/styles |
Public |
Local checkpoints and styles (no paths or secrets) |
GET /canvas/tags |
Bearer |
Tag vocabulary |
POST /canvas/jobs |
Bearer |
Submit an image job |
GET /canvas/jobs/{id} |
Bearer |
Job state |
GET /canvas/jobs/{id}/events |
Bearer |
Job SSE events |
GET /canvas/jobs/ws |
Socket |
Job updates over WebSocket |
POST /canvas/jobs/{id}/cancel retry |
Bearer |
Cancel or retry a job |
GET /sound/capabilities |
Bearer |
Sound backend capabilities |
POST /sound/import POST /sound/sessions POST /sound/ai |
Bearer |
Import audio, open a session, run an AI sound action |
GET /sound/waveform GET /sound/artifact |
Bearer |
Waveform data and rendered audio |
Voice
| Route |
Auth |
Purpose |
GET /voice/config |
Public |
Whether voice is on, plus the chosen voice (no secrets) |
GET /voice/state |
Bearer |
Voice server state |
POST /voice/tts |
Bearer |
Text to speech, proxied to the configured server |
POST /voice/stt |
Bearer |
Speech to text, proxied to the configured server |
POST /voice/install |
Bearer |
Install the local voice servers |
Browser automation
| Route |
Auth |
Purpose |
GET /browser/state |
Bearer |
Current browser session state |
POST /browser/action |
Bearer |
Perform a browser action |
POST /browser/replay |
Bearer |
Replay a recorded action sequence |
Automations
| Route |
Auth |
Purpose |
GET /automations PUT /automations |
Bearer |
List or save automations |
DELETE /automations/{id} |
Bearer |
Delete |
POST /automations/{id}/run cancel |
Bearer |
Run or cancel |
GET /automations/{id}/journal |
Bearer |
Run journal |
GET /automations/{id}/ws |
Socket |
Live run updates |
GET /automations/scheduler PUT /automations/scheduler |
Bearer |
Read or set the scheduler |
GET /automations/briefs |
Bearer |
Generated briefs |
POST /automations/briefs/{id}/seen |
Bearer |
Mark a brief seen |
GET /automations/palette |
Bearer |
Step palette for the editor |
POST /automations/try |
Bearer |
Dry-run a step |
GET /automations/connections PUT /automations/connections |
Bearer |
List or save API connections |
DELETE /automations/connections/{name} |
Bearer |
Remove a connection |
POST /automation/webhook/{id} |
Bearer + X-Maylyn-Webhook-Token |
Trigger an automation |
POST /hooks/{token} |
URL token |
Trigger a flow. The token is the credential |
Packs and mesh
| Route |
Auth |
Purpose |
GET /packs POST /packs |
Bearer |
List or create Packs |
POST /packs/join |
Bearer |
Join with an invite |
GET /packs/my-invite |
Bearer |
Your invite |
GET /pack-invites/{token} |
URL token |
Peek at an invite |
GET /packs/discover |
Bearer |
Public Pack directory |
GET /packs/{id}/state DELETE /packs/{id} |
Bearer |
State, or delete |
POST /packs/{id}/leave invite public join-request |
Bearer |
Membership actions |
POST /packs/{id}/devices |
Bearer |
Enroll a device |
POST /packs/{id}/devices/{node}/sharing location |
Bearer |
Device sharing and location |
DELETE /packs/{id}/devices/{node} |
Bearer |
Remove a device |
GET /packs/{id}/requests |
Bearer |
Pending join requests |
POST /packs/{id}/requests/{principal} |
Bearer |
Approve or deny a request |
GET /packs/{id}/jobs POST /packs/{id}/jobs |
Bearer |
List or submit Pack jobs |
GET /packs/{id}/jobs/{job} |
Bearer |
One job |
GET /mesh/peers |
Bearer |
Known peers |
GET /mesh/terms |
Bearer |
Participation agreement plus this machine's acceptance |
GET /mesh/terms/text |
Public |
Agreement text |
POST /mesh/terms/accept |
Bearer |
Accept the agreement |
GET /mesh/globe |
Public |
Coarse public Pack directory (name, ~20 km centroid, counts) |
POST /mesh/envelope |
Signed |
Node-to-node envelope |
POST /mesh/job-status |
Signed |
Requester pulls job results |
POST /mesh/register |
Signed |
Hub check-in. Answers not_a_hub unless running maylyn hub |
POST /internal/pool-relay/{token}/chat/completions |
URL token |
Pool relay, OpenAI-shaped completions |
Authority layer
| Route |
Auth |
Purpose |
GET /authority/status |
Bearer |
Authority layer status |
GET /authority/tasks |
Bearer |
Autonomous tasks |
POST /authority/delegations revoke |
Bearer |
Grant or revoke a delegation |
POST /authority/stop |
Bearer |
Emergency stop |
GET /authority/ledger |
Bearer |
Action ledger |
GET /authority/credentials POST /authority/credentials |
Bearer |
List or store credentials |
POST /authority/credentials/revoke |
Bearer |
Revoke a credential |
GET /authority/wallet |
Bearer |
Wallet status |
POST /authority/reauth |
Bearer |
Re-authenticate for a sensitive action |
See architecture/AUTHORITY_LAYER.md.
Achievements
| Route |
Auth |
Purpose |
GET /achievements |
Bearer |
Your achievements |
GET /achievements/leaderboard |
Bearer |
Leaderboard |
PUT /achievements/pinned |
Bearer |
Set pinned achievements |
Image backend (embedded canvas server)
When Maylyn Canvas runs in-process it also answers on its own listener with
GET /health, POST /generate, POST /edit, POST /upscale, and the
OpenAI-shaped POST /v1/images/generations, /v1/images/edits and
/v1/images/upscale. These are served by the image backend, not by the main
maylyn serve listener. See backend/canvas_embedded.go.
Profiling (opt-in)
Setting MAYLYN_PPROF=6060 (or 127.0.0.1:6060) starts a separate profiling
listener serving /debug/pprof/* and /debug/goroutines. It refuses to bind
anywhere but loopback and is not part of the main maylyn serve listener. See
backend/pprof.go.
Limits and hardening
- Loopback-only bind by default; widening it is a deliberate act.
- Conservative header, read and idle timeouts; bounded header size.
- Per-IP rate limiting with burst control;
429 with Retry-After: 1 when exceeded.
- Stream concurrency limit returns
429 with Retry-After: 5.
- Concurrency caps and time budgets on
/ask/stream and /chat/stream.
- Baseline security headers and panic containment on every response.
- Tools that require approval are refused over HTTP unless
MAYLYN_SERVE_ALLOW_TOOLS is set; read-only tools always work.
Design background: architecture/SECURITY_INVARIANTS.md.