The 8 principles
Apollia is a sovereign runtime for autonomous AI agents. It runs agents locally, under governance it enforces, with tools, and without a cloud dependency. Eight principles hold that promise together. They are not style preferences; each one was forced by a real constraint, and each one shapes a default you can rely on as an adopter. This page explains what each principle is and why it matters to you. It does not restate them as rules; for the rule form and its enforcement, the same eight appear as engineering constraints.
1. Local-first
No byte of user data leaves the machine without an explicit action. Inference can run fully local on a GGUF model, storage is a local SQLite file, and there is no telemetry and no phone-home. This exists because contractual guarantees were not enough for the organisations Apollia targets: the answer was not to promise the cloud would behave, it was to make the cloud technically unnecessary. For you, it means the safe posture is the default, not a setting you have to remember to turn on. The deeper treatment is in Sovereignty and local-first.
2. Zero external dependency
The binary runs on a clean Linux machine with no prior install: no Docker, no Node, no external database, no separate Python runtime. Every optional dependency on an external service degrades gracefully rather than breaking the run. This matters because operational complexity is a commercial veto for the teams evaluating a sovereign runtime. One artifact to deploy is one attack surface to reason about and one thing to keep running.
3. Minimal contract
An agent is a record: a mission, a model, tools, sources and rights. A package
writes it as an [[agents]] entry of its apollia.toml manifest, and no Python
object stands for it. Code is optional: a package brings tools (@tool) and
workflows (@workflow), two decorators over async functions, and nothing else.
The point is to keep the surface an author must learn small, so the runtime can
carry the hard parts (governance, budgets, tools) rather than pushing them into
every agent. It also keeps an agent in the hands of the person who uses it: what
a package declares is a set of defaults, and a tool, a right or a model is
changed from the interface without a new version of the code. For the exact
contract the Python of a package sees, read the SDK reference.
The runtime refuses a package whose manifest does not validate. A key the schema
does not know is refused with its full path, and so is what the schema cannot
express, such as an agent granted a package tool that the package does not
deliver. A misspelt key stops the package rather than leaving an agent running
without what its author declared. The earlier contract, a class decorated with
@agent holding @skill or @on_message methods, is gone (see
the agent contract).
4. Fail fast
Any error detectable at startup is detected at startup, not three steps into a run. A missing model, a malformed manifest, or an unreachable backend surfaces before work begins. This keeps failures cheap and legible: you learn what is wrong when you launch, not after the agent has spent part of its budget getting into a broken state.
5. One actor, one responsibility
The runtime is built as Tokio actors that each own their state and talk over message channels, with no shared locks between them. This is not an aesthetic choice: shared mutable state across async tasks is where deadlocks and unexplainable behaviour live. Keeping each actor responsible for one thing is what makes the system's behaviour something you can reason about. The strategy behind this is in Solution strategy.
6. Memory at agent initiative
Apollia never injects memory into an agent's prompt, and writes nothing to
memory on its behalf. An agent recalls what it decides to recall, when it
decides to, with the tool memory_search, and keeps a note with
memory_write. Both tools come from one gesture, checking Memory on the sheet
of the agent, and neither asks for consent: the space is the agent's own, one
space named after it and kept across its conversations and its tasks, which
reaches neither your data nor the machine. Automatic memory
injection is convenient and quietly corrosive: it makes a run's inputs opaque and
its behaviour hard to attribute. Leaving recall to the agent's initiative keeps
the record of what fed a decision honest.
Your profile is the opposite case. It follows you into every conversation it is
switched on for, so an agent never writes it. An agent whose Profile source is
checked may propose a fact with profile_propose, and the proposal is put to
you every time, with no "always".
Three exceptions exist, and none is reachable from an agent you install. The first two are confined to the built-in conversational assistant, the one behind the chat window, and live in its own prompt builder and chat manager. The third lives elsewhere, in the desktop prompt-rewrite command, which never touches the assistant's prompt at all: it answers the composer, not a run. No agent execution path goes through any of the three.
The first is the profile of the conversation, and it is the one you control. A conversation designates one of your profiles, or follows the default one. On every turn, at every autonomy tier, the assistant appends a short brief of that profile to its system prompt: name, role, function, tools, preferences and notes, whatever you filled in. It sits under a switch, "Apollia knows my profile", in the Integrations tab of the right panel of the conversation. The switch is on when a conversation is created, it is stored with the conversation, and turning it off takes effect on the next message. The context gauge of the composer says whether the prompt carries the profile, so what you read there is what the model received. Companion sessions and sessions that run an installed agent never receive it, switch or not.
The second has no switch, and is worth stating plainly. On the first message of a free chat session, the runtime searches an index of past session summaries with that message and appends up to three matches to the system prompt, under a heading naming them as previous conversations. Messages shorter than 20 bytes skip it, so a greeting recalls nothing. Only the summaries are injected, never past message content. Companion sessions are excluded outright: they must not inherit personal history.
Read that second one for what it is. Inside the chat window, a new conversation can start already carrying a trace of older ones, and the operator did not ask for it per session. It buys continuity in a product surface where a user reasonably expects to be remembered. With the profile brief, it is one of the two places in the runtime where the "at agent initiative" rule genuinely does not hold, and the only one you cannot turn off, which is why it is written here rather than left implicit.
The third exception is also operator-initiated, and happens only when explicitly triggered. When the operator clicks the "Improve prompt" button in a chat composer, the rewrite request includes three fields of the profile of the conversation (role, function, tools) if they have been filled. This is not automatic injection: nothing happens until the operator clicks, and they see the rewritten result before choosing to send it. The context helps the rewrite stay grounded in the operator's actual environment rather than inventing tools they don't use.
The memory layer itself is exportable and importable, which is why it belongs to sovereignty as much as to agency.
7. Non-negotiable safeguards
Every autonomous run is bounded by a step budget the runtime enforces: a ceiling
on reasoning steps, tool calls, and wall-clock time. An agent cannot raise or
remove it. Autonomy is only delegable if it has a hard edge, so
the edge lives in the runtime rather than in the agent's good intentions. This
page does not re-explain the mechanism; it is one of the pillars of the
accountability model. One honest detail: the
ceiling of an agent run is fixed in the code, and no key of apollia.toml
changes it. The full ledger of what is
partial is in Risks and technical debt.
8. Human CLI, machine API
Every surface is dual: a human reads a terminal, a machine reads JSON. A global
--json flag and TTY detection mean the same command serves an operator at a
prompt and a host product driving the runtime through its API. This exists
because Apollia is meant to be embedded, not only used, and an embeddable runtime
has to speak both languages without one compromising the other. The command
surface is the CLI reference; the machine surface is the
HTTP API reference.
Why these eight, together
Taken singly, each principle is a reasonable engineering call. Taken together, they are the value proposition: a runtime you can run without the cloud, deploy without a stack, delegate to without losing control, and embed without reverse-engineering. They are the reason autonomy here is something a regulated team can actually adopt, not just admire.