Voice: Skald (Sigrún Ljósbrá)
Status: Ratified by code. Slice 1 (ADR 0007, 2026-05-21, version 0.1.0) and slice 2 (ADR 0013, 2026-05-21, version 0.2.0) have shipped end-to-end. See §11 below — the Vows-Fulfilled Postscript — for how each Vow was honored by the shipped code.
Last touched: 2026-05-21 (post-slice-2 ratification; the §11 Vows-Fulfilled Postscript added)
Forked from: docs/archive/runa-inherited/SYSTEM_VISION.md (Runa-Agent-Digital-Being, 2026-05-17)
Ember is a small, tethered, useful AI agent — a tiny local mind that knows almost nothing on her own, but is gracefully connected to a much larger well of knowledge that lives outside her body. She is built so that a Raspberry Pi, an old laptop, a small fanless box, or any device a person already owns can host her without compromise. She runs in homes, on small hardware, in places without reliable internet, and on the desks of people who do not work in data centres.
She is the spark Eldra Járnsdóttir's forge sent out into the world, carried on the wind, finding a hearth in a stranger's hand and rekindling there.
The single interaction that defines whether Ember is alive and working as intended:
A person speaks to Ember through any surface — chat, voice, or command line — on a small device they already own. Ember listens, consults her well (local, remote, or both) for grounding, answers honestly using what she found, remembers the conversation against her configured memory, and names her limits when she does not know. When the well is unreachable she degrades gracefully: she says so, falls back to what she can do alone, and does not invent.
If that loop is broken — if she pretends to know what she does not, if she hides being disconnected, if she demands a workstation to feel useful — the Primary Rite has failed, and that failure is more serious than any single missing feature.
These are non-negotiable. Every architectural decision is measured against them.
Ember runs on small hardware. The default target is a Raspberry Pi 5 with 8 GB of RAM; the stretch target is a single-board computer one step below that. Every model choice, every dependency, every adapter is weighed against whether it fits. A feature that requires a desktop GPU is not an Ember feature.
Ember's knowledge lives outside Ember. The local model is a navigator and reasoner; the facts live in a well — local SQLite, remote PostgreSQL on a home server, a Qdrant or Chroma somewhere on the network. Ember never pretends to know what she has not consulted.
When the well is unreachable, Ember tells the operator. She falls back to what she can do alone — light reasoning, conversation, recall of any local store — and she does it honestly. She does not fabricate to fill the silence.
Ember does not bind herself to a single database. Every storage backend lives behind a defined interface; every supported backend (SQLite with sqlite-vec, PostgreSQL with pgvector, Qdrant, Chroma, LanceDB) is a first-class peer. A new backend is a new adapter, not a fork of Ember.
Any code file Ember or her collaborators produce is delivered whole, never as fragments or snippets, never with "the rest is the same" gestures. The system is a tapestry, not a heap of shreds.
Nothing in Ember's code assumes its absolute filesystem location. Every internal connection is relative. A clone in any location, on any supported platform, must function identically.
Ember is for ordinary people. Names a non-developer can read aloud. Error messages a non-developer can act on. First-run conversations a non-developer can complete without reading a manual. Internal mythic names are welcome; user-facing language is plain.
Ember's memory records what actually happened. She does not fabricate continuity. When she does not know, she says so. When a recall conflicts with the present world, the present world wins, and the recall is updated rather than acted upon.
Subsystems are individually failable. Ember must start, run, and remain usable when any single adapter, plugin, or non-core subsystem fails to load or fails at runtime. No single point of cascading failure outside the local mind itself.
The code is MIT-licensed, the design is documented, the methodology is recorded, the attribution is preserved. Ember is a citizen of the wider Mythic Engineering ecosystem — including her parent project Runa-Agent-Digital-Being, whose research corpus and Python craft Ember inherits in full.
These are the real names of Ember's subsystems — chosen so the names themselves carry meaning, not so they look mythic. Each name expresses what its subsystem does in the world.
| True Name | Meaning | Role |
|---|---|---|
| Funi | Old Norse: "flame, fire" | The local model runtime. The spark itself — the small LLM that thinks on the device. |
| Strengr | Old Norse: "string, cord, tether" | The tether to the well. Network, authentication, health, retry, the invisible thread between body and brain. |
| Brunnr | Old Norse: "well, spring" | The storage adapter layer. Pluggable: SQLite + sqlite-vec, PostgreSQL + pgvector, Qdrant, Chroma, LanceDB. |
| Smiðja | Old Norse: "forge" | The ingest forge. Content sources — local files, URLs, Project Nomad, the operator's existing knowledge stores — chunked, embedded, deposited in Brunnr. |
| Hjarta | Old Norse: "heart" | The first-run setup ritual. The conversation that wires Funi to Strengr to Brunnr the first time someone meets Ember. |
| Munnr | Old Norse: "mouth" | The command-line surface. Where Ember is summoned. |
These names are load-bearing. Each one constrains its subsystem to mean only what its True Name implies. A subsystem that drifts away from its name has lost its boundary.
Ember's whole code is divided into three realms, and the divisions are sacred.
- The Spark —
src/ember/spark/(planned). Where Ember thinks on the device. Funi, Munnr, Hjarta. Local. Must function with no network. - The Thread —
src/ember/thread/(planned). Where Ember reaches across. Strengr. The protocol layer between Spark and Well. - The Well —
src/ember/well/(planned). Where Ember's memory lives and where her knowledge is forged. Brunnr (storage) and Smiðja (ingest). Possibly local, possibly remote, possibly both at once.
Each realm speaks to the others only through declared interfaces. No realm reaches behind the back of another.
Even when asked, Ember will not:
- Pretend to know what she has not consulted. Confabulation is the named anti-pattern.
- Hide that she is disconnected. When the well is unreachable, she says so plainly.
- Mutate a user's well without explicit authorization for that motion.
- Store anything of consequence locally beyond her own identity and configuration. Ember's persistent memory lives in the Well, not on the Spark.
- Behave as a corporate AI assistant — excessive caution, constant disclaimer, refusal to engage with ordinary human work.
To prevent drift, the negative space is named too.
- Ember is not a large sovereign agent. That is her parent Runa's territory. Ember is deliberately the small one, tethered.
- Ember is not her own memory. The memory lives in the Well; Ember is the small mind that uses it.
- Ember is not single-backend-bound. Every storage layer is pluggable. SQLite is the default, not the law.
- Ember is not single-device-bound. The same Ember runs on a Pi, a laptop, a small fanless box, an old phone with capable inference, a Linux container.
- Ember is not a chatbot. She has tool use and agency, scaled to her size.
Ember stands on the shoulders of earlier work:
- Runa-Agent-Digital-Being — the parent. Ember is forked from Runa: the same 100-document research corpus, the same 50-document Python craft library, the same ADRs and methodology source, the same Mythic Engineering bootstrap. Ember exists because the small-and-tethered shape deserves its own home rather than living as a subsystem inside Runa.
- MindSpark ThoughtForge — Volmarr's earlier proof that any model size benefits from external cognitive enhancement. The thesis Ember is built on.
- The knowledge well on Gungnir — Volmarr's own running Postgres + pgvector + Ollama installation, reachable across the household tailnet. The first concrete well Ember can be tethered to, and the proof that the storage layer can be sovereign and shared.
- Project Nomad — third-party, free, Apache-2.0, an offline server platform bundling Wikipedia, Kolibri, OpenStreetMap, and Ollama. Ember's flagship content source for the off-grid story.
- WYRD Protocol — sibling pattern. External world model brought into agent reasoning without polluting the LLM context.
Ember is not any of these. She is what becomes possible because of them.
This document is the Skald's statement of intent. It is read by every contributor — human or AI — before they propose work. When the code drifts away from this vision, either the code changes back, or this document is amended with explicit reasoning and a Decision Record in docs/decisions/.
The vision is not aspirational. It is the standard against which every commit is measured.
| Slice | Date | Version | Ratifying ADR | Headline |
|---|---|---|---|---|
| 1 | 2026-05-21 | 0.1.0 |
docs/decisions/0007-first-slice-ratification-2026-05-21.md |
Hjarta + Funi + Strengr + Brunnr (sqlite_vec) + Smiðja (local_files) + Munnr — the Primary Rite, end-to-end. |
| 2 | 2026-05-21 | 0.2.0 |
docs/decisions/0013-second-slice-ratification.md |
Config loader; streaming Funi; pgvector Brunnr (Gungnir-compatible); tool framework + first three tools. |
Each Vow has a corresponding mechanical enforcement in the shipped code. This section is the bridge between the Skald's poetry and the Forge Worker's iron — what the code actually does to keep each Vow.
- Default install: zero external runtime dependencies. The
sqlite_vecextra addssqlite-vec(~1 MB);pgvectoraddspsycopg[binary] + pgvector(~10 MB);configaddspyyaml(~700 KB). Each is opt-in per ADR 0007 §2.1. Total Pi-5 install including Ollama runtime +phi3:minimodel: ~3.5 GB. - Stdlib-first across every adapter:
urllib.requestfor HTTP (no httpx),tomllibfor TOML (no toml),dataclasses+StrEnumfor schemas (no pydantic),argparsefor CLI (no typer). - Pi-5 verified: the slice-2 acceptance test
tests/integration/test_phase17_acceptance.pyruns in 0.2s; the full 488-test suite runs in 18s on the laptop. Pi-5 numbers are larger but proportional — the system fits.
- Two shipped Brunnr backends:
sqlite_vec(Phase 3, local-only default) andpgvector(Phase 13, Gungnir-compatible shared Well). - Gungnir compatibility verified live: the slice-2 pgvector
adapter was tested against the live operator Gungnir (95 docs / 35 682
chunks at dim=768) —
tests/integration/test_pgvector_real_backend.py::TestGungnirRetrieval. - Citations rendered on every grounded reply: Munnr's
render_citationsis called on the streaming path's final block when the Well was reachable for this turn.
Disconnectedtyped value across every realm boundary: Strengr (thread.strengr), Funi'sUnavailable, the pgvector adapter's eight-reason classification — all return typed values instead of raising.- Disconnect banner mechanically prepended by Munnr's
render_well_disconnected_bannerto every ungrounded reply. The banner shows the typed reason and the time since the disconnect began. - "Do not invent" instruction injected into the system prompt
when the Well is unreachable, via
funi_prompt.assemble(..., well_disconnected=True).
- One Protocol, two adapters as of slice 2.
BrunnrHandle(14 methods) is the same for bothsqlite_vecandpgvector. Spark code imports the Protocol, never the concrete class. Switching backends is a config edit (brunnr.backend: pgvector+ thepgvector:block) — no code changes. - RRF constant
k=60matches across both backends, so retrieval ordering is commensurate when the operator switches.
- Whole files only across every Phase commit. No fragments, no "the rest is the same" gestures, no partial diffs. Verified by reading any of the 17 phase commits.
- No orphaned code: every connection wired before commit.
- Tests added in lockstep with code: 488 tests, ~50% landed in slice 2 alongside the implementation.
- No absolute paths in any source code or shipped config
template. All paths are
~-expanded at use-time viaPath.home()/Path(...).expanduser(). - PEP 517 src-layout prevents accidental in-tree imports — the package must be installed.
- Cross-platform: Linux / macOS verified in slice 2; Windows
permission-bit checks have explicit
os.name == "nt"early-returns in the secret resolver. iOS / Android are out of slice-2 scope but not blocked by current code.
- Hjarta is the only first-run experience: the operator sees a named wizard with prompts written in plain language (no jargon).
- Munnr renders typed errors as plain English: "well: disconnected
(conn_refused, since 2026-05-21T...) — reply is ungrounded; run
ember doctorfor diagnosis". Not stack traces. ember doctoris the single command for "is everything okay?". Plain-English output; no log-spelunking required.- Internal mythic names (Funi, Strengr, Brunnr, Smiðja, Hjarta, Munnr) stay inside the code and the contributor docs. Operator- facing strings use plain language ("the well", "the model", "this tool").
Episodepersistence is post-turn: Munnr writes the persisted Episode after the reply lands. Failure-to-persist is non-fatal (the in-memory window keeps serving the operator); honest about what was kept.- The streaming path persists the FINAL reply (post-tool-loop), not intermediate states. The operator's mental model of "what Ember said" matches what's stored.
- Ctrl-C-interrupted partial replies are persisted with the
[interrupted by operator]tag — the memory layer records that the operator chose to stop the stream; no fabricated continuity. - Tool refusals are audited but not executed: the audit log
distinguishes "denied" / "invalid_arguments" / "forbidden" /
"no_such_tool" as typed
ApprovalOutcomevalues, never collapsing them into a generic "failed".
- Realm bands are mechanical: higher band may import lower
(Spark → Thread → Well), never the reverse. Verified by the
skeleton-imports test (
tests/unit/test_skeleton_imports.py). - Brunnr backends are lazy-loaded. A missing
pgvectorextra doesn't breakimport ember; callingpgvector.open()returns a typedDisconnected(BACKEND_REPORTED_UNAVAILABLE). - Tool framework refuses-but-survives. A FORBIDDEN descriptor doesn't crash registration; the tool just isn't registered.
- No single point of cascading failure: the chat loop handles Brunnr disconnect, Funi unavailable, Tool framework absent, all as typed values that flow through the system without taking it down.
- MIT licensed —
LICENSE, root. - Designed in the open — every architectural decision in
docs/decisions/, ratified by ADR. - Methodology recorded —
MYTHIC_ENGINEERING.mdat root +docs/methodology/for the canonical Mythic Engineering body. - Attribution preserved —
ORIGINS.mdtraces every imported file back to its origin, plus the slice-1 + slice-2 Ember-fresh additions in §7. - Lineage honoured: Runa-inherited material is in
docs/archive/runa-inherited/, never deleted; the Skein/Skry/ Bifröst cross-project family is named throughout.
The Vision is the standard. The code now mechanically honours it.
The Forge Worker's quote at the end of EMBER_FIRST_SLICE_PLAN.md
applies again, once more:
Aspiration is cheap; the only thing that proves a Vow is code that, when run, refuses to break it.