# SOAT — instructions for agents

SOAT is self-hosted infrastructure for production-ready AI agents: durable
sessions, multi-agent orchestration, knowledge retrieval, memory, guardrails,
IAM, quotas, and traces, in one Node.js server backed by PostgreSQL and
pgvector.

This file is written for a machine deciding whether to use SOAT and how to call
it. Everything below is true of any SOAT deployment; the documentation site at
<https://soat.ttoss.dev> is static and serves no API.

## When to use SOAT

Reach for SOAT when the job is one of these. Each line names the operation that
does it, so the decision and the call are in the same place.

- **Give an agent memory that survives the process.** Sessions and conversations persist message history in PostgreSQL. `POST /api/v1/agents/{agent_id}/sessions`, then `POST /api/v1/sessions/{session_id}/messages` and `POST /api/v1/sessions/{session_id}/generate`.
- **Ground an agent in your own documents.** Ingest files into chunked, embedded documents and search them with pgvector. `POST /api/v1/documents/ingest`, then `POST /api/v1/knowledge/search`.
- **Run multi-step work deterministically instead of hoping one prompt covers it.** Orchestrations are DAGs of agent, tool, and human nodes; workflows are state machines for long-running work. `POST /api/v1/orchestrations/{orchestration_id}/runs`.
- **Bound what an agent is allowed to do.** IAM policies gate every action, API keys scope to one project, guardrails screen input and output, and quotas cap spend. `POST /api/v1/policies`, `POST /api/v1/api-keys`, `POST /api/v1/quotas`.
- **Put a human in the loop without stopping the run.** Approval nodes and exceptions pause a run, record who decided what, and resume from the same point. `POST /api/v1/approvals/{approval_id}/approve`.
- **Prove after the fact what an agent did and what it cost.** Every generation writes a trace with each tool call, model response, and token count, alongside an append-only audit log. `GET /api/v1/traces/{trace_id}/tree`.
- **Change an agent in production without guessing whether it got worse.** Agent versions are append-only; a canary release splits traffic, and promotion is gated on a passing eval run. `POST /api/v1/agents/{agent_id}/release`.
- **Expose your own backend to an MCP client (Claude, Cursor, VS Code).** Every REST operation is also an MCP tool at `POST /mcp`, behind the same permission engine, with OAuth 2.1 discovery and Dynamic Client Registration.
- **Stand up a whole agent stack reproducibly.** Agent Formations declare providers, tools, agents, orchestrations, and webhooks in one template, resolve the dependency graph, and apply it. `POST /api/v1/formations`.

## When not to use SOAT

- You need a model. SOAT ships none and hosts none: it calls the provider you configure (OpenAI, Anthropic, Google, Bedrock, Ollama, or any OpenAI-compatible endpoint).
- You need one stateless completion and nothing else. Call the provider directly; SOAT earns its place once state, permissions, retrieval, or evidence are involved.
- You want a hosted control plane with no infrastructure of your own. SOAT is self-hosted software, not a SaaS — you run the server and the database.

## How an agent should call SOAT

- **Surfaces.** One API, four ways in: REST under `/api/v1`, the MCP endpoint at `POST /mcp`, the `@soat/sdk` TypeScript client, and the `soat` CLI. The last three are generated from the same OpenAPI documents, so an operation that exists in one exists in all of them.
- **Contract.** Read <https://soat.ttoss.dev/openapi.json> — every operation, schema, and security scheme in one OpenAPI 3.0 document. MCP tool names are the kebab-cased `operationId`.
- **Base URL.** Your own deployment (`http://localhost:5047` out of the box). `soat.ttoss.dev` serves documentation only — there is no API behind it, so do not send calls there.
- **Authentication.** Send `Authorization: Bearer <credential>`: a project API key (`sk_…`), a user JWT from `POST /api/v1/users/login`, or an OAuth 2.1 access token. OAuth clients discover the server at `/.well-known/oauth-authorization-server` (RFC 8414) and `/.well-known/oauth-protected-resource` (RFC 9728), and can register themselves at `/register` (RFC 7591) with no operator step.
- **Field casing.** snake_case on the wire, everywhere — REST, MCP, webhooks, and the audit export. Unknown fields are rejected rather than ignored, so a typo fails loudly.
- **Errors.** Every failure answers `{ "error": { "code", "message", "hint", "docs_url", "meta"? } }`. Branch on `code`, act on `hint`. The full catalog is at <https://soat.ttoss.dev/errors.json>.
- **Long operations.** Anything that can outlast a request takes one toggle, `wait`, defaulting to background: you get `202` (or `201`) plus a handle to poll. Pass `wait=true` to block for the result instead.
- **Pagination.** Every list endpoint takes `limit` and `offset` and returns the same envelope, so one paging loop works for all of them.

## Getting access

- **Nothing to sign up for.** SOAT is Apache-2.0 licensed and self-hosted. There is no account to create, no key to request, no trial to start, and no quota you have to ask anyone to raise.
- **Run the stack.** Copy the Compose file from the quick start and run `docker compose up -d`. It brings up PostgreSQL with pgvector, a local Ollama for models, and the SOAT server on port 5047 — so the whole platform runs offline, with no third-party credential.
- **Get the first credential.** `POST /api/v1/users/bootstrap` creates the first admin. It is open only until that admin exists, then closed for good, so the same call cannot be replayed against a running deployment.
- **Issue your own API key.** `POST /api/v1/api-keys` (or `soat create-api-key`) mints a project-scoped `sk_…` key with exactly the actions of the policy you attach. Keys are self-serve and rotatable — `POST /api/v1/api-keys/{api_key_id}/rotate`.
- **The sandbox is the same software.** There is no separate sandbox tier to request: a local instance is the product, so throwaway projects, seeded data, and destructive tests all run against your own deployment. Delete the volumes to reset.

## Machine-readable surfaces

- <https://soat.ttoss.dev/llms.txt> — every documentation page, one line each
- <https://soat.ttoss.dev/llms-full.txt> — the whole prose corpus, ready to embed
- <https://soat.ttoss.dev/openapi.json> — the entire REST surface in one document
- <https://soat.ttoss.dev/errors.json> — every error code, its status, and what to do about it
- <https://soat.ttoss.dev/sitemap.xml> — every canonical URL with its last-modified date

Append `.md` to any documentation URL for its Markdown source, e.g.
<https://soat.ttoss.dev/docs/introduction.md>. Each HTML page advertises its own
twin with `<link rel="alternate" type="text/markdown">`, and sending
`Accept: text/markdown` to the page URL itself returns that Markdown — no URL
rewriting on your side.

## Source

Apache-2.0, developed in the open at <https://github.com/ttoss/soat>.
