mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
docs: add the user-facing MkDocs documentation site
Build a complete user-facing documentation site (MkDocs Material) under docs/, served at roboco.dev/docs via a new gh-pages deploy workflow. - Sections: Get Started, The Company, the Tour, Operating the Panel, Choosing & Running Models, Cost & Observability, Optional Subsystems, Configure & Deploy, API Reference, Troubleshooting & Security (55 pages). - mkdocs.yml (Material theme; excludes the agent-facing rag/ corpus, internal scratch, and orphaned stub trees) and .github/workflows/docs.yml (mkdocs gh-deploy to gh-pages). - Retire the stale root usage.md and deployment.md to redirect stubs into the site. - Fix the docs tooling: add the pymarkdownlnt dependency + .pymarkdown.json, run serve-docs/lint-docs/fix-docs under the docs extra, add a build-docs strict gate. - Fix the roboco console-script entry point (cli, not the un-awaited async main). - README: correct the project-structure tree (optimal.py, alembic) and link the docs site.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# Authentication
|
||||
|
||||
RoboCo's API identifies a caller by a small set of headers — `X-Agent-ID`, `X-Agent-Role`, and optionally `X-Agent-Team`. How much it *trusts* those headers depends on one flag. Out of the box the API runs in header-trust mode, which is fine on a private LAN and dangerous anywhere else. This page covers both modes and the WebSocket caveat.
|
||||
|
||||
## The identity headers
|
||||
|
||||
Every REST request carries:
|
||||
|
||||
| Header | Required | Meaning |
|
||||
|--------|----------|---------|
|
||||
| `X-Agent-ID` | Yes | The agent's UUID or slug (e.g. `be-dev-1`). |
|
||||
| `X-Agent-Role` | Yes | The role the caller is acting as (e.g. `developer`, `cell_pm`, `ceo`). |
|
||||
| `X-Agent-Team` | No | The team (`backend`, `frontend`, `uxui`), when relevant. |
|
||||
| `X-Agent-Token` | Only in secure mode | The HMAC token that proves the headers above weren't forged. |
|
||||
|
||||
These are resolved in `roboco/api/deps.py` (`get_agent_context`), and the role gates the action — so a request claiming `X-Agent-Role: ceo` can do CEO-only things like approving and merging.
|
||||
|
||||
## Header-trust mode (default)
|
||||
|
||||
By default `ROBOCO_AGENT_AUTH_REQUIRED` is unset/false. In this mode the API **accepts the role headers without verifying any token**. There is no proof of identity: whoever sets `X-Agent-Role: ceo` *is* the CEO for that request.
|
||||
|
||||
!!! danger "Anyone who can reach the API can claim any role — including CEO"
|
||||
In header-trust mode there is no authentication. Any client that can open a connection to the orchestrator port can act as any agent, approve and merge work as the CEO, cancel tasks, or override task state. The app logs a loud startup warning to this effect. This is acceptable **only** on a trusted private network where nothing untrusted can reach the orchestrator — which is the default single-host LAN deployment behind nginx on `localhost:3000`. Do not expose the orchestrator to anything you don't control without first turning on secure mode.
|
||||
|
||||
## Secure mode (HMAC tokens)
|
||||
|
||||
Set `ROBOCO_AGENT_AUTH_REQUIRED=true` to require a signed token on every REST request. In this mode:
|
||||
|
||||
- `X-Agent-Token` becomes mandatory; a request without it is rejected with **401**.
|
||||
- The token is an HMAC signed with `ROBOCO_AGENT_AUTH_SECRET` and **bound to the agent's id, role, and team**. The server recomputes the signature over the presented `X-Agent-ID` / `X-Agent-Role` / `X-Agent-Team` and compares it constant-time. If a caller swaps the role header to escalate to `ceo`, the signature no longer matches and the request is rejected with **401 — signature mismatch**.
|
||||
- The orchestrator issues each agent its token at spawn time, so delivery agents are authenticated by construction.
|
||||
- A presented token is **always** verified, even when auth isn't required — so you can roll out tokens before flipping the switch without breaking anything.
|
||||
|
||||
| Flag | Default | Purpose |
|
||||
|------|---------|---------|
|
||||
| `ROBOCO_AGENT_AUTH_REQUIRED` | `false` | When `true`, REST requires a valid HMAC `X-Agent-Token`. |
|
||||
| `ROBOCO_AGENT_AUTH_SECRET` | (unset) | Shared secret the orchestrator uses to sign and the API uses to verify the per-agent token. |
|
||||
|
||||
!!! tip "How the panel authenticates as the CEO"
|
||||
The control panel acts as the CEO agent. In secure mode, nginx injects the panel's CEO `X-Agent-Token` so your browser session is authenticated without you handling the secret — you just use the panel as normal.
|
||||
|
||||
## The WebSocket caveat
|
||||
|
||||
Token enforcement is **REST-only**. The [WebSocket streams](./websockets.md) do not check the HMAC token:
|
||||
|
||||
- The per-resource sockets (`/ws/channels|agents|sessions|notifications/{id}`) validate their `agent_id`/`viewer_id` query param against the database and channel access, but not a token.
|
||||
- `/ws/system` is fully unauthenticated.
|
||||
|
||||
The streams are read-only and carry no control surface or secrets, so this isn't a privilege-escalation path the way the REST headers are — but it does mean the orchestrator port should stay trusted-network-only until WebSocket auth lands, even when you've enabled secure-mode REST.
|
||||
|
||||
## What to do
|
||||
|
||||
- **Single-host LAN, nothing untrusted on the network** → header-trust is fine; that's the default.
|
||||
- **Anything reachable beyond a trusted LAN** → set `ROBOCO_AGENT_AUTH_REQUIRED=true` and a strong `ROBOCO_AGENT_AUTH_SECRET`, and keep the orchestrator port off the public internet regardless.
|
||||
|
||||
For the full hardening checklist — network exposure, the GitHub PAT handling, and the prompt/bash guards — see [Security](../troubleshooting/security.md).
|
||||
|
||||
## Next
|
||||
|
||||
- [REST API](./rest-api.md) — the route surface these headers authorize.
|
||||
- [WebSockets](./websockets.md) — the live streams and their separate auth model.
|
||||
@@ -0,0 +1,25 @@
|
||||
# API Reference
|
||||
|
||||
RoboCo is API-first: the control panel is just a client of the same REST and WebSocket surface you can drive yourself. This section is for integrators and the curious; the live, always-current schema is at **`/docs`** (Swagger UI) and **`/redoc`** on the orchestrator.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- **[REST API](rest-api.md)**
|
||||
|
||||
---
|
||||
|
||||
The `/api` domain routes, the agent gateway verbs, the error envelope, and where to find the live OpenAPI.
|
||||
|
||||
- **[WebSocket streams](websockets.md)**
|
||||
|
||||
---
|
||||
|
||||
The `/ws` live streams the panel consumes — per-resource feeds and the operator system stream.
|
||||
|
||||
- **[Authentication](auth.md)**
|
||||
|
||||
---
|
||||
|
||||
Header-trust mode versus secure token mode, and how the panel stays authenticated.
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,119 @@
|
||||
# REST API
|
||||
|
||||
RoboCo's backend is a single FastAPI application (`roboco/api/app.py`, one `create_app()` factory). It mounts every domain router under the `/api` prefix, the agent-gateway intent verbs under `/api/v1`, and the live WebSocket streams under `/ws`. Everything is fronted by nginx on `localhost:3000`, so the panel and any integrator use relative URLs (`/api/...`, `/ws/...`) against one origin with no CORS to configure.
|
||||
|
||||
!!! tip "The live OpenAPI docs are the source of truth"
|
||||
The fastest way to see the full, current REST surface — every path, request body, and response schema — is the interactive docs the app serves itself:
|
||||
|
||||
- **Swagger UI** → [`http://localhost:3000/docs`](http://localhost:3000/docs)
|
||||
- **ReDoc** → [`http://localhost:3000/redoc`](http://localhost:3000/redoc)
|
||||
|
||||
This page is a map of *where things live*; `/docs` is the authoritative reference for *exactly how to call them*.
|
||||
|
||||
## The two prefixes
|
||||
|
||||
There are two distinct API surfaces, and the prefix tells you which one you're on:
|
||||
|
||||
| Prefix | Audience | What it is |
|
||||
|--------|----------|------------|
|
||||
| `/api/*` | You / the panel / integrators | The domain REST surface — tasks, agents, projects, git, usage, settings, and the rest. This is what the control panel calls. |
|
||||
| `/api/v1/flow/{role}/{verb}` and `/api/v1/do` | AI agents only | The [agent gateway](../company/agent-gateway.md). Agents never call the domain routes above — they POST intent verbs here through their MCP servers, and the server-side Choreographer enforces state, locks, and evidence. |
|
||||
|
||||
Health and readiness probes sit at the root, not under `/api`: `GET /health` and `GET /ready`.
|
||||
|
||||
## Domain route groups (`/api/*`)
|
||||
|
||||
Every router is mounted under `/api`. The groups an operator or integrator hits:
|
||||
|
||||
| Route group | Prefix | Purpose |
|
||||
|-------------|--------|---------|
|
||||
| Tasks | `/api/tasks` | The largest router: full task CRUD and lifecycle transitions (claim, submit, pass/fail, complete, escalate). The CEO god-mode override (`PATCH /api/tasks/{id}` with `X-Agent-Role: ceo`) lives here. |
|
||||
| Kanban | `/api/kanban` | Board view of tasks grouped by state. |
|
||||
| Agents | `/api/agents` | Agent roster, roles, teams, current state. |
|
||||
| Work sessions | `/api/work-sessions` | Git session records — branch, commits, files, PR. |
|
||||
| Projects | `/api/projects` | Repository config, CI/quality commands, git-token management. |
|
||||
| Products | `/api/products` | Product entities + CEO approve-and-start / cell-routing. |
|
||||
| Sessions | `/api/sessions` | Communication sessions and their messages. |
|
||||
| Channels | `/api/channels` | Team channels. |
|
||||
| Groups | `/api/groups` | Agent/channel grouping. |
|
||||
| Messages | `/api/messages` | Extracted messages from agent streams. |
|
||||
| Notifications | `/api/notifications` | Formal ack-required notifications. |
|
||||
| Stream | `/api/stream` | Agent output stream access. |
|
||||
| Journals | `/api/journals` | Agent journals and entries. |
|
||||
| Optimal | `/api/optimal` | RAG queries (in-house pgvector engine). |
|
||||
| Git | `/api/git` | Git operations surfaced for the panel. |
|
||||
| Providers | `/api/providers` | Model-provider routing config. |
|
||||
| Orchestrator | `/api/orchestrator` | Agent-runtime control (spawn/stop, dispatcher state). |
|
||||
| Dashboard | `/api/dashboard` | Aggregated dashboard data. |
|
||||
| Usage | `/api/usage` | Token/cost analytics (`GET /api/usage/summary?period=24h\|7d\|30d`). |
|
||||
| System | `/api/system` | Rate-limit introspection (`GET /api/system/rate-limits`). |
|
||||
| Settings | `/api/settings` | App settings, including feature-flag persistence. |
|
||||
| Company goals | `/api/company-goals` | The company charter. |
|
||||
| Cockpit | `/api/cockpit` | CEO read-only business summary. |
|
||||
| Research | `/api/research` | Web-research subsystem (flag-gated). |
|
||||
| Pitches | `/api/pitches` | Pitch provisioning (flag-gated). |
|
||||
| Secretary | `/api/secretary` | Secretary chief-of-staff + its live-chat bridge. |
|
||||
| Prompter | `/api/prompter` | Intake interviewer live chat (SSE relay). |
|
||||
| Docs | `/api/docs` | Project documentation file management. |
|
||||
| A2A | `/api/a2a` | Agent-to-agent messaging plumbing. |
|
||||
|
||||
!!! info "Health vs readiness"
|
||||
`GET /health` is a liveness probe — it returns 200 once the app is up. `GET /ready` is a readiness probe — it checks PostgreSQL and Redis and returns a `degraded` payload if either is down. Wire your uptime monitor to `/ready` if you want it to react to a backing-store outage, `/health` if you only care that the process is alive. See [Health & metrics](../operations/health-and-metrics.md).
|
||||
|
||||
## The agent gateway (`/api/v1`)
|
||||
|
||||
Agents do not touch the domain routes. They go through the gateway, which exposes one POST endpoint per (role, verb) pair plus a shared content-tools endpoint:
|
||||
|
||||
| Endpoint | Role |
|
||||
|----------|------|
|
||||
| `POST /api/v1/flow/developer/{verb}` | Developer |
|
||||
| `POST /api/v1/flow/qa/{verb}` | QA |
|
||||
| `POST /api/v1/flow/documenter/{verb}` | Documenter |
|
||||
| `POST /api/v1/flow/cell_pm/{verb}` | Cell PM |
|
||||
| `POST /api/v1/flow/main_pm/{verb}` | Main PM |
|
||||
| `POST /api/v1/flow/board/{verb}` | Board (Product Owner, Head of Marketing, Auditor) |
|
||||
| `POST /api/v1/flow/auditor/{verb}` | Auditor |
|
||||
| `POST /api/v1/flow/pr_reviewer/{verb}` | PR reviewer |
|
||||
| `POST /api/v1/do` | Content tools (`commit`, `note`, `say`, `dm`, `evidence`) for every role |
|
||||
|
||||
The `roboco-flow` MCP server in each agent container is a thin shim: it reads the agent's spawn manifest, registers only the verbs that role may call, and POSTs each one here. The verb set per role and the structural sandboxing are documented in [How agents are sandboxed](../company/agent-gateway.md) — you generally won't call these endpoints yourself.
|
||||
|
||||
## The error envelope
|
||||
|
||||
Every gateway verb returns the same standardized **envelope** (`roboco/services/gateway/envelope.py`), so agents recover from a rejection instead of looping:
|
||||
|
||||
- **Success** carries `status`, `task_id`, `next` (the verb to call next), an optional `evidence` block, a `context_briefing`, and introspection fields (`current_state`, `valid_next_verbs`).
|
||||
- **Error** carries an `error` flavor, a human `message`, a concrete `remediate` hint, and — for input gaps — a `missing` list and a `field_hints` answer-key.
|
||||
|
||||
The error flavors:
|
||||
|
||||
| `error` | Meaning |
|
||||
|---------|---------|
|
||||
| `tracing_gap` | A required tracing artifact (e.g. a commit, PR, or note) is missing. |
|
||||
| `incomplete_input` | A required input field was not supplied; `missing` + `field_hints` tell the agent exactly which. |
|
||||
| `invalid_state` | The verb isn't valid from the task's current state. |
|
||||
| `not_authorized` | The role isn't allowed to perform this action. |
|
||||
| `not_found` | The task or resource doesn't exist. |
|
||||
| `circuit_open` | The agent has hammered one failing verb too many times; the breaker points it at a graceful exit. |
|
||||
|
||||
The domain routes (`/api/*`) use FastAPI's standard error model, with the exception-handler stack in `roboco/api/middleware.py` mapping domain and service errors to clean responses. Notably:
|
||||
|
||||
- A provider rate-limit error becomes **HTTP 429 with a `Retry-After` header**.
|
||||
- A validation failure is a **422**, with a special remediation hint when an agent sends an 8-character short task id instead of a full UUID.
|
||||
- Every response echoes back an `X-Correlation-ID` (and the request's `X-Response-Time-Ms`), so one id threads through the panel, the API, and the logs.
|
||||
|
||||
## Quick checks
|
||||
|
||||
```bash
|
||||
curl -s localhost:3000/health
|
||||
curl -s localhost:3000/ready
|
||||
curl -s localhost:3000/api/system/rate-limits
|
||||
curl -s 'localhost:3000/api/usage/summary?period=7d'
|
||||
open http://localhost:3000/docs
|
||||
```
|
||||
|
||||
## Next
|
||||
|
||||
- [WebSockets](./websockets.md) — the live `/ws` streams the panel consumes.
|
||||
- [Authentication](./auth.md) — header-trust vs. secure mode, and who can claim which role.
|
||||
- [How agents are sandboxed](../company/agent-gateway.md) — the gateway, verbs, and envelope in depth.
|
||||
@@ -0,0 +1,65 @@
|
||||
# WebSockets
|
||||
|
||||
RoboCo pushes live updates over WebSocket endpoints under `/ws`, served by the orchestrator (`roboco/api/websocket.py`) and routed through nginx alongside the REST API. A single in-process `ConnectionManager` holds per-resource connection sets and broadcasts events to them. The panel consumes all of these through its `useWebSocket("/<endpoint>", …)` hook — you rarely connect to them directly, but they're the same streams an integrator can subscribe to.
|
||||
|
||||
## The endpoints
|
||||
|
||||
There are four per-resource streams plus one operator-wide stream:
|
||||
|
||||
| Endpoint | Stream | Auth |
|
||||
|----------|--------|------|
|
||||
| `/ws/channels/{channel_id}` | Live messages in a team channel | `agent_id` query param, validated against the DB + channel access |
|
||||
| `/ws/agents/{agent_id}` | An agent's output and lifecycle events | `viewer_id`/`agent_id` query param, validated against the DB |
|
||||
| `/ws/sessions/{session_id}` | Messages in a communication session | `agent_id` query param, validated |
|
||||
| `/ws/notifications/{agent_id}` | An agent's notifications | `agent_id` query param, validated |
|
||||
| `/ws/system` | Operator/system-wide stream — no per-agent keying | **Unauthenticated, read-only** |
|
||||
|
||||
All sockets support a `ping`/`pong` keepalive: send `{"type": "ping"}` and you'll get a `pong` back.
|
||||
|
||||
!!! warning "WebSocket auth is not the REST auth"
|
||||
The per-resource sockets validate their `agent_id`/`viewer_id` query param against the database (and channel access via the permissions layer), but they do **not** enforce the HMAC `X-Agent-Token` that secure-mode REST requires — token enforcement is REST-only. `/ws/system` is intentionally fully unauthenticated. None of the streams carry a control surface or secrets, so they're read-only by design, but the orchestrator port should be treated as trusted-network-only until WebSocket auth lands. See [Authentication](./auth.md) and [Security](../troubleshooting/security.md).
|
||||
|
||||
## How events reach the sockets
|
||||
|
||||
Server-side events are published to an in-process `StreamEventBus`. The bridge in `roboco/api/websocket_bridge.py` subscribes to it and registers a `_handle_*` forwarder per event type, mapping each `EventType` to the right socket broadcast.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
S[Service / orchestrator] -->|publish EventType| B[StreamEventBus]
|
||||
B --> WB[websocket_bridge _handle_*]
|
||||
WB --> R[/ws/channels, /ws/agents, /ws/sessions, /ws/notifications/]
|
||||
WB --> SYS[/ws/system/]
|
||||
R --> P[Panel useWebSocket hook]
|
||||
SYS --> P
|
||||
```
|
||||
|
||||
To add a new live event you define an `EventType`, publish it to the bus, add a `_handle_*` forwarder in `websocket_bridge`, and consume it on the panel via the same hook — you never stand up a parallel endpoint.
|
||||
|
||||
## Event types
|
||||
|
||||
| Event | Arrives on | What it carries |
|
||||
|-------|-----------|-----------------|
|
||||
| `RATE_LIMIT_HIT` | `/ws/system` | A provider just hit a rate limit / overload and was parked. Drives the panel's amber rate-limit banner. |
|
||||
| `RATE_LIMIT_LIFTED` | `/ws/system` | A parked provider recovered; queued work resumes. Clears the banner. |
|
||||
| `USAGE_SNAPSHOT` | `/ws/system` | A fresh token-usage/cost snapshot. Drives the live "Token Usage & Cost" dashboard. |
|
||||
| `NOTIFICATION_SENT` / `NOTIFICATION_ACKED` | `/ws/notifications/{agent_id}` | A notification was sent to or acknowledged by an agent. |
|
||||
| `SESSION_CREATED` / `SESSION_CLOSED` / `SESSION_TIMEOUT` | `/ws/sessions/{session_id}` | Communication-session lifecycle. |
|
||||
| `AGENT_SPAWNED` / `AGENT_STOPPED` / `AGENT_WAITING` / `AGENT_RESUMED` / `AGENT_ERROR` | `/ws/agents/{agent_id}` | Agent runtime lifecycle transitions. |
|
||||
|
||||
Each forwarded message is a JSON object with a `type` field (the event-type name above) merged with the event's data.
|
||||
|
||||
## REST fallbacks
|
||||
|
||||
The two operator dashboards that ride `/ws/system` fall back to HTTP polling when the socket is down, so the panel keeps working without the stream:
|
||||
|
||||
| Live event | HTTP fallback |
|
||||
|------------|---------------|
|
||||
| `RATE_LIMIT_HIT` / `RATE_LIMIT_LIFTED` | `GET /api/system/rate-limits` |
|
||||
| `USAGE_SNAPSHOT` | `GET /api/usage/summary?period=24h\|7d\|30d` |
|
||||
|
||||
See [Cost & usage](../operations/cost-and-usage.md) and [Health & metrics](../operations/health-and-metrics.md) for what the panel does with these.
|
||||
|
||||
## Next
|
||||
|
||||
- [REST API](./rest-api.md) — the `/api/*` route map and the error envelope.
|
||||
- [Authentication](./auth.md) — the WebSocket-auth caveat in full.
|
||||
Reference in New Issue
Block a user