A Smart Node is one machine. On it run a set of named services, one Postgres cluster, and a registry that says what exists. Everything else, including every agent, is a row in that registry. None of it is set up by hand.
That sentence is the whole design, and the rest of this page is what it means in practice.
The entities
The system knows a fixed number of kinds of thing. Each kind has exactly one home. Not "usually lives here" but "exists only here". Adding an agent means adding a row, and the directory, the memory space, the group membership and the permissions all follow from that row. None of them is a separate act of setup.
| Entity | Declared in | Also materialised as |
|---|---|---|
| Organisation | sn_config.tenant_profile | The business the node works for, as the agents read it |
| Person | sn_config.contacts, contact_channels | The identity a message is resolved against. People inside the business are sn_config.users |
| Agent | sn_config.agents | A directory under /srv/agents/, a memory space, a Unix group |
| Role | sn_config.specialists | Reusable text defining a job, which any agent can be created from |
| Project | sn_config.projects, project_members | A directory under /srv/projects/, a Unix group, and the agents in it |
| Task | the node's planner | A unit of work with an owner and a state |
| Objective | the node's planner | The parent a task hangs from |
| Document | the node's document store | A file under version control |
| Memory | a workspace in the memory service, plus sn_config.agent_knowledge | What an agent recalls across conversations |
| Message | sn_core.messages | An append-only bus row plus its delivery state |
| Channel | sn_config.channels, agent_channels | A binding between an address and an agent |
| Tool | sn_config.tools, granted via agent_tools | An executable body plus its usage text in the prompt |
| Policy | sn_config.policies | Text composed into the prompts of agents in that category |
| Secret | the node's encrypted store | A credential a component resolves when it runs, never printed |
| Event | sn_core.events | An append-only audit record |
This is the difference between a system and a wrapper around a model call. A wrapper has no entities. It has a prompt and a key.
Three kinds of thing are deliberately absent, because on this node they have no home yet: a dataset, meaning machine output kept out of semantic search; a short-lived experiment that is meant to expire; and a register of backups. Until each is declared, it is not something the node knows about, and this page will not pretend otherwise.
One thing this table does not claim. The homes above are where each kind is declared and stored, not a wall around it. Group membership on a project directory is enforced by the operating system; the per-agent path lists in an agent's definition are instructions in its prompt, and are not a boundary.
The perimeter
Everything above sits on the customer's machine, inside the dashed boundary in the diagram. Mail is polled over IMAP, Telegram is long-polled, Slack runs over an outbound socket: every channel dials out, so none of them needs a port opened toward the node. What you publish beyond that is a firewall decision on your own machine. Details on install.
What leaves: the model call, and whatever the channels you enabled must reach in order to send.
What happens to one message
There is not one path. There are four, and which one runs is decided by the channel the message arrived on. The commonest is the wake loop, which handles mail, Slack and internal traffic. The numbers below are the numbered badges in the diagram.
- An adapter polls the channel and writes the message into
sn_core.messages. Arrival and handling are separate steps. - The recipient is resolved by matching
recipientagainst the channel bindings inagent_channels. Internal traffic matches an agent's slug directly. No routing table, no routing column: the answer is a query. - The dispatcher claims that agent and wakes it. The claim fails closed, so two wakes cannot run at once.
- The prompt is composed from the registry: the agent's own definition, the usage text of each tool it holds, the rules for its categories. Not read from a file a person maintains.
- The model call goes out through the gateway.
- The reply is written back as an outbound row and a timer sends it. Every outgoing message exists as a record before it exists as an email.
The other three paths: a fast path for chat channels where latency matters, a synchronous path for web and panel chat, and a flow path where the runner drives a sequence of steps. They differ in how the agent is invoked, not in where anything is stored.
Where things are stored
One Postgres cluster. The three schemas below carry everything this page has described; a fourth holds extensions. The document store and the planner are separate databases on the same cluster.
| Schema | Holds |
|---|---|
sn_config | The registry: agents, roles, projects, channels, tools, policies, people, integrations |
sn_core | The domain: messages, delivery state, events, model sessions |
public | Login |
sn_core.messages and sn_core.events are append-only, enforced by a database trigger rather than by convention. Grants, policy changes and permission changes are written into the same event log as everything else, so the record of what an agent was allowed to do sits next to the record of what it did.