Soma Portal Specs
Project specs page. Visit RAIZOR for product & sales info.

Technical specs  ·  Soma platform

The anatomy of Soma,
in engineering terms

How the Soma platform is built: the full stack, how a request moves through it, how data is stored and kept apart, how each layer is secured, and where AI does the work. Written for engineers and technical evaluators. The plain-language overview tells the same story with the biology left in.

soma/ one body: sense → integrate → act → remember

8 servicesone compose file, one Linux box
1 write pathevery change goes through /act
250+ actionseach a typed kind with its own schema
111 tablesrow-level security forced
3 model classesan admin picks a route, not a model
1 identitythe Slack workspace

01 · ArchitectureHow it fits together

Read the schematic top to bottom as one request's journey. A request enters through Slack or a browser, crosses the only public surface (Caddy), and lands on the Portal or the Thalamus. Agent work is queued, picked up by a stateless worker that reasons with a model behind a privacy-aware gateway, and every effect it wants goes through the Action Layer: the single gate that writes to the Cortex and dispatches to the outside world. Results come back as events.

efferent · intent and commands afferent · results as events read · query, never gated identity · sign-in

Swipe sideways to see the whole schematic →

SENSORY SURFACE · WHERE A REQUEST ARRIVES LONG-TERM MEMORY Slack mentions, DMs, threads identity authority Soma Portal dashboards · Synapse chat Sign in with Slack Venture hosts a branded portal each a skin, never access Public surfaces intake forms · site chat narrow, rate-limited Caddy · TLS · hostname router only ports 80 and 443 are published Portal · SvelteKit on Node Dashboards, admin, audit view, the Synapse chat read-only database login · every write goes to /act Thalamus · Slack router verifies signatures, picks the agent no /act token · no model key claims tasks · loads seat, skills, packs exec-agent ×2 · stateless workers loads the seat, retrieves, runs the tool loop Python · FastAPI · hybrid retrieval every tool call becomes a POST /act LLM gateway TrustedRouter, primary OpenRouter, fallback privacy floor on every call Action Layer · POST /act The only write path: validates, authorizes, audits, dispatches. JSON Schema per kind · 250+ kinds · 7 callers, each fenced Nexus · remote operator private network only time-limited grant repo ops need a co-sign event + state change, one transaction after commit EFFECTORS n8n multi-step automations webhooks via Caddy holds no /act token Workspace + Slack Drive, mail, calendar governed connections credentials stay here CRM + tasks GoHighLevel, ClickUp provider hosts pinned per kind MCP + web MCP tools, web research TeamCentral over MCP PDFs rendered in-house The Cortex Postgres 16 + pgvector RLS forced, tenant per row Canonical state 111 tables read by anyone allowed, written by /act alone Task queue Slack + Synapse jobs Seats + skills agents, versions Knowledge packs vector(1536), HNSW full-text + vector published only Scope tree + grants org → venture → department → workflow grants inherit down Events log append-only: no UPDATE, no DELETE what changed, when, on whose authority results return here

One Linux server runs everything above except the model providers and the SaaS tools at the bottom. The services share an internal network with no route to the internet. Only the services that must reach outside (Portal, Thalamus, agent workers, Action Layer) and the proxy also join an egress network; the database has no way out at all.

02 · StackThe full stack

Eight containers from one compose file, plus one host service. There is no ORM anywhere: every service speaks plain SQL to Postgres, so what runs is what you read.

ComponentTechnologyWhat it does
Edge
ProxyCaddy 2TLS for every host (automatic certificates for GSC hosts; a Cloudflare origin certificate for the venture domain). Reads the hostname and routes to the Portal, the Thalamus or n8n. The only published ports.
PortalSvelteKit 2 · Svelte 5 · NodeThe whole product surface: dashboards, client register, agents, admin, audit, the Synapse chat. Tailwind with daisyUI. Reads the Cortex with a read-only login; sends every write to /act.
ThalamusPython 3.12 · FastAPISlack Events API, slash commands and interactive payloads. Verifies Slack's signature, de-duplicates events, picks the agent, writes a task, posts the reply. Holds no write token and no model key.
Reasoning
Agent workersexec-agent ×2 · Python 3.12Identical, stateless replicas. Claim a task, load the seat (persona, grants, skills, packs) from the Cortex, retrieve, run the tool loop, render documents to PDF in-process.
LLM gatewayTrustedRouter → OpenRouterOpenAI-compatible routers. TrustedRouter is primary and carries a minimum-privacy setting on every request; OpenRouter is the fallback member. Which model serves is data, set per class by an admin.
Embeddingstext-embedding-3-small1,536 dimensions. Knowledge-pack chunks are embedded when they are ingested; questions are embedded when they are asked.
Action and memory
Action LayerPython 3.12 · FastAPI · psycopg 3The single write path. A registry of typed action kinds, each with a JSON Schema (Draft 2020-12) and a policy, plus one handler module per family. Writes the event and the state change together, then dispatches.
The CortexPostgreSQL 16 + pgvector111 tables of canonical state, an append-only events log, the task queue and the knowledge-pack vectors (HNSW, cosine). Row-level security is enabled and forced on tenant data.
Schema changesPlain SQL, up and down250+ numbered migrations, each paired with a verify script that proves it landed. No migration framework: each one is applied through a gate and verified against production.
Effectors and host
Automationsn8nMulti-step workflows (lead-generation intelligence, error handling). Runs in the same compose file and holds no /act credential of its own.
ConnectionsAPI · webhook · MCP · CLIGoogle Workspace, Slack, GoHighLevel, ClickUp, TeamCentral (over MCP), web research and others, each a governed connection. Each action kind declares which connection type it needs and which provider hosts it may reach.
Render sidecarNode · headless ChromiumA standby HTML-to-PDF renderer on the internal network only, with no route out. Day-to-day PDFs are now rendered inside the agent workers.
Repo runnersystemd serviceThe executor behind the repository actions (push, deploy, rollback). Runs on the host, sandboxed by systemd, and every operation it performs needs a human co-sign.
SecretsBitwarden Secrets ManagerSecrets are rendered into the environment file at deploy time (owner-only permissions, previous copy kept). The repo holds key names only, checked nightly against what is configured.

03 · Request lifecycleThe life of one Slack question

Someone mentions an agent in Slack and asks for something that needs both thinking and doing. The same path serves the Synapse chat in the Portal; only the first two steps differ.

  1. Slack delivers the event

    The Thalamus checks Slack's HMAC-SHA256 signature in constant time, rejects anything older than five minutes, and drops a repeated event id.

    sense
  2. The right agent is chosen AI

    An explicit @agent wins; otherwise a router model call picks one specialist agent the person is allowed to reach. The Thalamus writes a task to the queue and nothing else.

    route
  3. A worker claims it and loads the seat

    One of the two stateless workers takes the task and reads the agent's persona, version, grants, skills and knowledge packs from the Cortex.

    load
  4. Retrieval AI

    The question is embedded and matched against published knowledge-pack revisions by full-text and vector search together, merged by reciprocal rank fusion; the top six chunks go into context.

    integrate
  5. The tool loop AI

    The model answers with either a reply or one tool call as a JSON line. Up to six steps and seven minutes per run. Tool results come back inside fenced markers, with any forged markers stripped first.

    reason
  6. Every tool call is a governed action

    Each call becomes a POST /act as the agent, carrying the run's envelope. The Action Layer runs its full check (below). High-impact kinds are parked for a human to approve instead of executing.

    act
  7. Commit, then dispatch

    The event row and the state change are written in one transaction. Only after the commit does the handler reach the outside world: n8n, Google, the CRM, an MCP server.

    execute
  8. Results come home

    What the outside world returns is recorded as events too, so the Cortex never drifts from reality. The Thalamus posts the answer back to the Slack thread, and the run's trace and model usage are metered.

    remember

Inside /act: eleven checks, in order

Every write in the platform, from a person clicking Save to an agent's tool call, passes the same pipeline. A failure at any step stops the request with nothing written.

01 · Caller

Service token

The calling service presents its own bearer token, compared in constant time. Seven callers exist, each with its own token.

02 · Caller policy

What that caller may send

Each caller is fenced to certain authorities and kinds. The public intake form, for example, may submit exactly one kind.

03 · Actor

Who is acting

The person, agent or operator is resolved; expired and revoked identities are refused.

04 · Policy

May they do this

The union of the actor's capability bundles must allow this kind.

05 · Schema

Is the payload valid

Checked against the kind's JSON Schema stored in the registry. Unknown fields are refused, not ignored.

06 · Scope

Is the target in reach

The thing being changed must sit inside the branch of the scope tree the grant covers.

07 · Protection

Protected agents

A remote operator can never modify an agent marked protected.

08 · Co-sign

Two keys for code

Repository push, deploy and rollback are parked until a human co-signs.

09 · Agent gate

How far an agent may go

Each skill binding has a ceiling: recommend, draft, trigger a low-risk action, or needs approval (parked for a person).

10 · Commit

Event + change, together

The audit event and the state change land in one transaction, or neither does.

11 · Dispatch

After the commit

Side effects in the outside world run only once the record exists.

04 · DataHow data is stored and reached

One Postgres database is the single source of truth. Dashboards, agent context and reports are views of it, never copies that could disagree. Isolation is enforced by the database itself, not by application code remembering to filter.

Canonical

State tables

111 tables: clients, projects, tasks, people, agents, skills, connections, reports, structure and access. Every tenant row carries its tenant_id.

Memory

Append-only events

Every change is a typed event naming what changed, when and on whose authority. The writer role has no UPDATE or DELETE on the table and its policies allow only reads and inserts, so history cannot be quietly rewritten.

Isolation

Row-level security, forced

RLS is enabled and forced on tenant tables, so even the table owner is filtered. Reads are limited to the tenants a database login is mapped to; writes must match the tenant set for the transaction. A weekly check fails if any table slips.

Knowledge

Vectors beside the records

Knowledge packs are versioned. Their chunks are stored with 1,536-dimension embeddings under HNSW cosine indexes, so agents search meaning and keywords in the same database that holds the facts.

Who may touch what

Each service connects with its own database login, granted only what its job needs.

ServiceReadsWrites
Action LayerIts tenants' dataEverything, through typed events; inserts only on the events log
Agent workersIts tenants' dataTheir own run records, model usage, retrieval log and task status. No business data.
ThalamusIts tenants' dataThe task queue only
PortalIts tenants' dataNothing. Every change is a call to /act.
OperatorsWithin their grantNothing directly. Personal logins that expire on a date, created by the reconciler.

One tree, and everything hangs off it

Scopes

Organization → venture → department → workflow. Pages, permissions, connections and scheduled reports all hang off nodes of this tree. A grant is made on a node (principal, scope, capability bundle, expiry) and reaches every node beneath it. Writes are checked against that branch.

Archived and hidden

Two independent flags. Archived is retired: nothing new may be created under it. Hidden is not published yet: fully operational, scheduled work still fires, simply invisible to anyone without the right to manage structure.

Venture hosts

A hostname maps to a venture and sets the brand skin and starting context. It never grants access: what you may see is resolved from your grants on every request, whichever address you arrived at.

One reconciler

A single process owns database roles, tenant mappings and network access lists, and is the only thing allowed to change them. Access drift has one place to look.

05 · SecurityDefense in depth

Soma assumes that any single control can fail: a stolen session, a misbehaving agent, a poisoned document. Each layer below works on its own, so one failure does not open the next.

EdgeOutermost

Only HTTPS answers from the internet, through Caddy, with HSTS and the server banner removed. The database and the Action Layer are never published; they are reachable only on the internal network or the private operator network.

IdentityWho you are

Sign in with Slack (OpenID Connect) with state and nonce checks and a workspace check: membership in the GSC workspace is the credential. The session is an HMAC-signed cookie with a fixed seven-day life, and the person's access is re-resolved from the Cortex every fifteen minutes.

AuthorizationWhat you may do

Grants on the scope tree, capability bundles per role, and the eleven /act checks. Roles come from the database on every request, never from the page or the hostname.

Service fencesWhat each part may do

Seven /act callers, each with its own token and its own list of allowed kinds. The Slack router holds no write token or model key. Public callers are rate-limited and reach a handful of kinds at most.

Agent containmentWhat AI can touch

Agents act only through /act, as themselves. Skills narrow a capability by kind and bind it at a ceiling; high-impact kinds wait for a person. Provider hosts are pinned per kind, agents cannot invoke other agents, and tool output is fenced as data.

DatabaseThe last word

Forced row-level security per tenant, an append-only events log, and a separate login per service with only the grants its job needs. Migrations run as a separate admin role that no service holds.

Secrets and auditInnermost

Secrets live in a secrets manager and are rendered at deploy time; the repo carries names only. Every action is an event, every model call is metered, every server error is recorded as an event naming the action that failed.

The remote operator is a guest, not a superuser. Nexus, the outside consultant agent, has its own caller identity and reaches the platform only over a private network. Its grant is tenant-scoped and expires on a date, it cannot touch protected agents, its repository actions wait for a human co-sign, and one step revokes all of it.

06 · AIWhere AI does the work

Models do the parts that need judgment and language: choosing the right agent, reading knowledge, deciding the next step, writing the answer. Plain code does everything that has to be exactly right: identity, permissions, validation, the write itself and the audit trail.

JobWhat the model doesHow it is bounded
RoutingPicks one specialist agent for a question that names noneOnly agents the person may reach are candidates; agents never call agents
RetrievalEmbeds the question for meaning searchPublished pack revisions only; full-text and vector results fused, top six
Tool loopDecides the next step: answer, or call one tool26 effect tools, six steps, seven minutes; each call is a checked /act
Synapse chatAnswers from the page you are on, streamed as it writesSame queue, same worker, same gate as Slack
Public site chatAnswers visitors on a venture's websiteEach visitor is a throwaway identity that may only chat and leave feedback
Agent BuilderNone: people build agents, skills, packs and playbooksEvery agent and skill is versioned; one canonical copy of each skill

Model classes and TrustedRouter: route, not model

An agent never names a model. It asks for a class (fast, standard or deep) and the platform picks the first healthy member of that class. A member is a router, a route and a privacy floor together, because zero data retention is a property of the endpoint that serves the call, not of the model's name.

Swipe sideways to see the whole diagram →

THE ASK MODEL CLASSES · DATA, NOT CODE ROUTERS UPSTREAM exec-agent needs a model for this step asks for a class fast quick drafts, high volume standard the everyday workhorse deep complex structuring Certification gate 5 of 5 tool-format calls before a route may serve · ≥2 per class failover TrustedRouter primary gateway privacy floor sent on every call: any no_store zdr confidential zero-retention pool where data must not be kept; fails closed OpenRouter fallback router, next member in the class's failover order Model providers frontier and open models, each route chosen by an admin per class the endpoint, not the model name, decides what is retained usage metered per call

Defenses against prompt injection

Data stays data

Tool results and retrieved text are wrapped in fenced markers, and any look-alike markers inside them are stripped before the model sees them.

Words can't widen power

Whatever a document says, the agent can only call the kinds its skills allow, at the ceiling they were bound with, checked again by /act.

Pinned destinations

Each kind that reaches the internet declares the provider hosts it may call, so a planted URL can't redirect a request.

Narrow public context

On public surfaces only an allowlisted slice of page context reaches the model, and output into Slack is escaped.

07 · OperationsHow it is run

One working system first, proven end to end, then the next capability grown onto it. Every new capability arrives the same way: a node on the tree, a typed action, a policy for who may call it, and a test that fails when it breaks.

Deploy

Health-gated rebuilds

Each service is rebuilt from the repository and must pass its health gate before traffic returns. A push is not a deploy; a schema change is applied through its own gate and verified against production.

Tests

Proof, not presence

27 Playwright end-to-end specs with accessibility checks, around 190 service test files, a verify script for every migration, and a weekly sweep of the whole bar on the live box.

Observe

Built-in telemetry

Server errors recorded as events, a heartbeat for every scheduled job, health and pulse pages in admin, a watchdog timer, per-call model usage and full run traces.

Schedule

Reports as beats

A scheduler ticks every five minutes. Internal reports fire, render, deliver by email or Slack and archive what was sent, generated the same way so the two copies cannot disagree.

Recover

Nightly backups

A nightly database dump, the automation engine's data and a mirror of the repository, alongside the server's own snapshots.

Grow

A path to a second box

Because every row carries its tenant, a venture that outgrows the shared platform can be promoted to its own stack without untangling its data.

Five rules the architecture enforces

One write path

Nothing writes to the Cortex except the Action Layer. Not the Portal, not an agent, not an operator.

Reads are free, writes are events

Anyone allowed may read; every change is a typed, validated, audited event.

Route, not model

Retention is a property of the endpoint, so admins choose routes with a privacy floor, never a bare model name.

The hostname is a skin

Where you arrived sets the look. What you may see comes from your grants.

Everything hangs on the tree

Pages, permissions, connections and schedules are nodes and grants on one structure, not separate lists.

Interested in collaboration or a custom installation?

I design and build team portals like Soma for organizations that want their own. Tell me what you have in mind and I'll reply by email.

Get in touch →
This project was designed by Douglas Campbell and is maintained by RAIZOR. SOMA · PORTAL SPECS · NOVEMBER 2026