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:
| Client | Package | What it is |
|---|---|---|
| The command line | cli/ | The ivralo command and its subcommands, built on Typer |
| The session | tui/ | The full-screen terminal session a plain ivralo opens, built on Textual |
| The local API | api/ | 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:
| Layer | Where | What it holds |
|---|---|---|
| Knowledge | skills/, skills.local/ | Skills: markdown packages that say when to use a technique, the hard rules and worked code |
| Capability | src/ivralo/engine/ | The library the skills call: documents, decks, workbooks, PDFs, charts, SQL over files, OCR |
| Agents | src/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.
| Key | Agent | On when |
|---|---|---|
main | The main agent | Always |
analyst | Data Analyst | By default |
spreadsheet | Spreadsheet Builder | By default |
visualizer | Chart Designer | By default |
reporter | Report Writer | By default |
scribe | Document and Deck Writer | By default |
librarian | Librarian | The knowledge index or the web tools are on |
pdf_operator | PDF Operator | By default |
courier | Courier | Mail or a chat channel is configured |
secretary | Secretary | Google Workspace is configured |
github | GitHub Clerk | A GitHub token is configured |
coder | Code Engineer | The workspace is a git repository |
investigator | Incident Investigator | The 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_MODE | An unattended run may also |
|---|---|
developer (default) | Nothing |
server | Commit, 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:
| Where | What |
|---|---|
| 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
| Package | What 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.