A sovereign local brain in Python. The datasets are the mind — the models are only the handlers. Runs on a tired laptop. Signs what it knows.
NEW IN v0.3.0 — GOPHER NET · TUI + SESSIONS · STREAMING · LEARNING GATES · ONE-COMMAND INSTALL
* the goal: serving is retrieval. Models compile knowledge in the background; they never serve at query time. How it gets there ↓
Clone, install, run. No expertise needed.
Every question falls down the ladder until something answers with confidence. No freelancing. Tags on answers are honest: [local] for everything served on-box, [oracle] for cloud — with the measured tokens/sec, capped at 499.
Math in Python, never the model. Whitelisted AST — division by zero returns undefined, not a hallucination.
Pattern-matched answers from signed cassettes. Taught facts outrank learned ones; learned outrank trained.
Vector search over everything the node has curated and learned. Paraphrases welcome.
A small model via Ollama for what retrieval can't answer. Its answers are candidates for cassettes — the next ask costs zero.
OpenRouter / Nvidia for the hard ones. Personal data is stripped before anything leaves the building; keys are one-time-use, never stored.
The goal, stated plainly: serving is retrieval. Models act as background compilers — research, distillation, promotion — not at serve time. A question you ask every day should cost zero model tokens, because the answer already lives in a cassette or in memory.
Every model answer is a candidate cassette, so the next ask costs zero. Learning is gated — see below — but serving is never slowed by the gates.
A relevance classifier alone promotes on shape, never truth — a confident confabulation scores like a fact. So promotion passes two gates before anything is written.
Factual promotes need one verification call. Unsure votes NO. A NO — or a failed judge — blocks learning, fail-closed.
Before writing, the pair is compared against memory and AIML. A genuine disagreement blocks the write and files a curiosity gap, so research can resolve it. Elaborations are not conflicts.
After 10 quiet minutes, the heartbeat triggers one research cycle. A failed ask becomes a curiosity task; curiosity becomes research; research becomes a cassette. The model works the night shift so retrieval can work the day shift.
Background research stays Wikipedia-only. Idle time is the training budget — time replaces parameters.
A full-screen curses interface — /tui in the REPL. Same brain as the line REPL, which stays as fallback.
/new /sessions /resume /delete — one file per chat, titled from its first question, auto-saved on every line. Resume by prefix or id.
Model tiers stream real chunks. Retrieval answers reveal at a simulated 499 t/s. Ctrl-C cancels — the partial answer is kept, never learned. Enter redirects mid-answer.
Multiline input, slash-command autocomplete (Tab), history (Up/Down), Ctrl-C interrupt.
It should feel like gopher — retro but super useful. Every peer is a public key; every post, message, and bundle is signed. /gopher in the REPL.
Peer IDs are re-derived from the public key — a card can't lie about who it is. Address is a hint; the key is the proof.
Nodes find each other through a signed nodelist — a well-known URL or shipped with a release — plus gossip: exchanging peer lists. The whole list is refused if any entry fails verification.
One signed JSON file per post. Bad signatures are rejected, never displayed.
Signed direct messages. No mail to unfiled strangers — the recipient must be a peer first. Inbox verifies on load.
Interactive rooms, one signed file per message, polled every 2 seconds. Two terminals sharing a room talk live.
/upload signs a bundle into the drop; /download verifies before merging. Signed cassettes travel the same way.
Given a context and the current ask, trim keeps the lines the ask needs and drops the rest — verbatim, in order, nothing rewritten. The selection is made by a 712-tree gradient-boosted model (1.2 MB, ~7 ms on one core). It is not a language model: it spends no model tokens, it just scores lines. That is why it belongs on the cloud path — it shrinks what the oracle has to read. It fails closed: a blank query, a tiny input, or a cut too small to matter returns the context unchanged.
Below is a live replay: the input is a real 302-line server log (excerpt shown), the outputs are the real results of ceti.trim on that log. Pick a question.
Trim is wired in exactly two places: the trim tool, and the agent loop — where it trims tool output before the oracle summarizer reads it, so the error at the tail survives and the summarizer reads ~100 tokens instead of ~5,000. The oracle sees fewer tokens because trim cut the noise first.
The oracle doesn't just answer — it does. /task in the REPL hands a cloud model the tool registry and a sandbox.
Pure-Python Linux namespaces: read-only system, no network, private /work, rlimit caps. Foreground and background jobs. Code never runs on the host.
run_python, background jobs, file ops jailed to the work root, session notes, trim compression. JSON-schema manifests; OpenAI function-calling export for the oracle.
SKILL.md bundles — markdown + code, loaded like cassettes. file-ops, sandbox-python, web-fetch, sqlite ship in the seed set.
Drop a file into data/library/ and it lands in vector memory — chunked, embedded, deduplicated. /ingest runs it.
Oracle paper writer: story bible first, ~200-word blocks, rolling summaries, library chunks as style reference (never reproduced).
Node identity in data/identity.dat (0600, never leaves the box). Dataset releases are signed, chained, Merkle-rooted — verified before merging.
Exponential backoff on transient cloud failures; HTTP 4xx fails fast. No API key? The loop refuses loudly instead of pretending.
One command. No expertise needed.
Re-running install keeps an existing custom node name unless you pass --node-name= explicitly. The node name is the agent's display name — separate from the fingerprint prefix.
A fake shell with real answers. Try help.