Skip to content
ivralo
Sections

Docs / Contributing

Architecture

How ivralo is put together: a core library with three clients, three layers inside the core, agents built from presets, and state in PostgreSQL. Read this before opening the code.

A core and three clients

ivralo is a Python library, the core, in src/ivralo/. Everything it can do lives there: the agents, the tools, storage, incidents, jobs, the egress policy. Three thin clients sit on top of it:

ClientPackageWhat it is
The command linecli/The ivralo command and its subcommands, built on Typer
The sessiontui/The full-screen terminal session a plain ivralo opens, built on Textual
The local APIapi/ivralo serve: HTTP on 127.0.0.1 only, behind a token and an X-Ivralo-Api-Version header

The rule is that no capability lives in a client. A client draws, reads input and calls the core; logic two clients need moves into the core. Tests hold the line: the core imports without Typer or Textual, nothing outside cli/ and tui/ imports a client, and every API route is a call to a core function the other clients can reach too. That is why the API could be added as a third client without a second implementation of anything.

   ivralo (cli)      ivralo (tui)      ivralo serve (api)
         \                 |                  /
          +---------- the core ---------------+
          |  agents   tools   storage   incident   jobs   egress  ... |
          +-------------------------------------------------------+
                  |                                 |
           PostgreSQL (state)              the workspace (a git repository)

Three layers

Inside the core, three layers are kept apart:

LayerWhereWhat it holds
Knowledgeskills/, skills.local/Skills: markdown packages that say when to use a technique, the hard rules and worked code
Capabilitysrc/ivralo/engine/The library the skills call: documents, decks, workbooks, PDFs, charts, SQL over files, OCR
Agentssrc/ivralo/agents/The agents, deliberately thin

Expertise belongs in a skill, not in an agent's instructions. A long instruction string is loaded whether or not it is needed and is invisible to tests; a skill can be versioned, installed, linted and measured. An agent sees a one-line index of the shelf and loads a skill's full text only when a task needs it, which is what keeps the context small enough for a mid-sized local model.

Agents are presets

Every agent is built by one factory from a preset: a key, a display name, a grant set, one hint sentence that says what it is for, a tool-call limit, and whether its Python may write files. A preset has no prompt of its own. Every agent gets the whole skill shelf and the same house rules, so what makes one agent different from another is what it may do, not what it is told.

Each built agent is a model, a Python runtime in a worker process of its own, and the shelf.

KeyAgentOn when
mainThe main agentAlways
analystData AnalystBy default
spreadsheetSpreadsheet BuilderBy default
visualizerChart DesignerBy default
reporterReport WriterBy default
scribeDocument and Deck WriterBy default
librarianLibrarianThe knowledge index or the web tools are on
pdf_operatorPDF OperatorBy default
courierCourierMail or a chat channel is configured
secretarySecretaryGoogle Workspace is configured
githubGitHub ClerkA GitHub token is configured
coderCode EngineerThe workspace is a git repository
investigatorIncident InvestigatorThe workspace is a git repository

A local skill that no preset owns becomes a preset of its own, granted what its own frontmatter declares.

The main agent is the one you talk to. It holds what the team as a whole may do, less reading accounts (Google, GitHub, databases) and filing incidents, under the same approvals and limits as everyone else. It answers a request itself when it can and hands work on only for a fresh context or a narrower grant: to a specialist, or to an instance, a copy of a preset opened with a brief and its own context that returns a report. An instance never holds more than whoever opened it. IVRALO_MAIN_GRANTS narrows the main agent; nothing widens it.

A grant is a named permission such as code.edit, shell.full or git.commit. Every agent has a baseline of eight (Python, reading and writing workspace files, asking you, asking or handing work to a colleague, reading a configured database, fetching stock photos); a preset adds to it, and the Investigator gives some of it up. Capabilities lists them.

Modes are grant sets

Developer mode and server mode are not two programs. A mode is the set of grants an unattended run holds, a run with nobody there to approve anything:

IVRALO_MODEAn unattended run may also
developer (default)Nothing
serverCommit, and push the branch ivralo made for a draft pull request

The mode is read from the environment and the home's .env only, never from a repository's .ivralo/.env, so a repository cannot grant itself a push.

Where state lives

All conversational state is in PostgreSQL, in the ivralo schema: sessions, run history, long-term memory, the knowledge index (with pgvector) and jobs. Pointing a second machine at the same database brings back the same conversations. The database is one you name, Docker's compose service, or a cluster ivralo makes under the home; see PostgreSQL.

On disk there is configuration, and the workspace's own working files:

WhereWhat
The home.env, permissions.json, mcp.json, brand.json, identifiers.json, skills.local/, the trusted folders, the API token
The workspace's .ivralo/File history, change records, plans, the code index (SQLite), incidents and their reports, changesets, recordings

.ivralo/ is added to .git/info/exclude, so none of it reaches a commit. State has the full list.

The packages

PackageWhat it holds
config/, settings/Settings from the environment and .env; the person's own files
llm/Model references and construction for every provider
storage/Everything in PostgreSQL
skills/The two shelves: loading, installing, lint
agents/Presets, the factory, the main agent, limits
tools/What an agent can do: the Python worker, files, the shell and its policy, git, verify, worktrees, changesets, mail
egress/The one host policy for web, browser, worker and shell
incident/, jobs/Investigations, alert intake, and persistent jobs
lsp/, index/, code/Language servers, the code index and the code reading tools
workflow/, scheduling/Plan mode and scheduled requests
lineage/, replay/Where a claim came from; recording and replaying a run
engine/The document and data library
evals/The eval runner; see How ivralo is measured
cli/, tui/, api/The three clients

The repository's CLAUDE.md is the index of the code, with one file per area under .claude/rules/. The reasons behind the shape are in the decision records; see Decisions.