Architecture
The MCP server is one factory function mounted by the registry at
/mcp/:capsuleId. The public product surface is HTTP: dashboard, REST API, and Streamable HTTP MCP.
Storage
Section titled “Storage”Everything durable lives under CAPSOL_DATA_DIR. Each capsule keeps the same plain-file .capsol/ layout internally. No database.
| Concern | On disk | Owned by |
|---|---|---|
| Built-in entries | knowledge/<scheme>/<path>.{md,json} or skills/<path>.md | DotfileStore |
| Custom entries | entries/<scheme>/<path>.<ext> | DotfileStore |
| Per-agent memory | memory/<agent>.json | MemoryStore |
| Audit log | logs/YYYY-MM-DD.jsonl | EventLogger |
| Binary payloads | blobs/<sha256> | DotfileStore |
- Writes atomic (temp-file +
rename(2)). - Reads plain file reads.
- Boring on purpose. Nothing to corrupt.
The server factory
Section titled “The server factory”createServer(opts) returns an MCP server with the capsol tools registered:
interface CreateServerOptions { boxRoot: string; // path to .capsol/ memoryDir?: string; // defaults to <boxRoot>/memory cwd?: string; // working dir reported to clients shareConfig?: { // present in registry mode role: "reader" | "appender" | "writer" | "owner"; uri_prefixes?: string[]; // allow list denied_prefixes?: string[]; // deny list };}The registry resolves the share, verifies its token when present, passes shareConfig into the factory, and wraps storage with FilteredStore.
FilteredStore
Section titled “FilteredStore”One-way mirror. Wraps the base store and:
- Rejects writes the share’s role doesn’t permit.
- Hides reads not matching the allow list or matching the deny list.
- Deny wins on conflict.
Downstream (DotfileStore, filesystem, audit log) doesn’t know whether it’s filtered. That asymmetry is the point of the one-factory design — no duplicated storage code for scoping.
Memory
Section titled “Memory”- Per agent, keyed off MCP
clientInfo.name. - Flat string→string map in
memory/<agent>.json. - Atomic writes (same temp + rename).
- Never surfaced to other agents. Never pushed on
deploy. - Identifier is isolation, not security. Two agents sharing a name share memory — expected.
Audit trail
Section titled “Audit trail”Every tool call emits one jsonl line:
{"ts":"2026-04-21T10:42:00Z","agent":"claude-code","action":"write","uri":"docs://api","version":3,"metadata":{"connection_id":"csl_7f2a","principal_id":"prn_..."}}Append-only. Date-based rotation. Retention is a cron job, not a capsol concern. Registry exposes recent events via GET /v1/capsules/:id/logs (owner only) and a dashboard SSE feed.
Registry
Section titled “Registry”registry/server.ts is an Express 5 app wrapping the MCP factory with:
- Auth. Operator login for the dashboard/admin API, OAuth MCP grants, approved enrollment credentials, and hash-only credential storage.
- Rate limits. Three buckets,
429+Retry-Afteron overflow. - Grant spine. OAuth, dashboard-created connections, and agent enrollments all flow through
GrantRequestRecordbefore an MCP credential becomes usable. - Persistence.
boxes.json,shares.json,credentials.json,principals.json, OAuth clients, grants, access profiles, approval policy, and enrollment/cursor records. - Health.
GET /healthandGET /ready. - Dashboard. SPA talking to
/api/*and/v1/*.
No database, no job queue, no workers. Host a single Node process, host capsol.
Trust boundary
Section titled “Trust boundary”Capsule content is untrusted. The server wraps every read with a risk-class-dependent preamble and a provenance footer (writer, commit-at-write, cited anchors). Neither band is optional or writer-controlled.
See Trust.
Intentionally absent
Section titled “Intentionally absent”- No embeddings. Structured URIs, not vector chunks.
- No database. Plain files.
- No background jobs. Synchronous within the request.
- No multi-tenant billing. Self-hosted; billing is the operator’s problem.
Reference
Section titled “Reference”src/server/server.ts—createServerfactory.src/server/tools.ts— tool schemas.src/storage/dotfile-store.ts— primary store.src/storage/filtered-store.ts— role + prefix wrapper.src/storage/memory-store.ts— per-agent memory.registry/server.ts— HTTP shell.registry/auth.ts,registry/rate-limit.ts— auth + limits.