Huginn/README.md
Meta Vens c74dd530de daemon: embeds through the endpoint pool, GPUs together and the CPU last
query-embed-on-cpu, follow-up daemon-embedder-gpu-pool: huginn-daemon takes
repeated --ollama-url (GPU hosts) and --reserve-ollama-url (CPU hosts) and
builds the same EmbedderPool as huginn-sources, through one ollama::pool
constructor. A lone --ollama-url is a pool of one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 07:56:22 +01:00

99 lines
5.8 KiB
Markdown

# Huginn
Huginn is the GameCult memory organ. It owns each agent instance's mind as
typed state: what an instance remembers, which of that memory is admitted to
steer its next action, and the provenance of every admitted claim.
Upstream: `https://github.com/GameCult/Huginn.git`
**Status.** Live: `huginn-mind` (admission with receipts, typed queries, derived status), `huginn-daemon` (CultNet RUDP with a Qdrant semantic index), and the `huginn` CLI from `crates/eureka-state` (whoami, view, query, admit, schema). The `eureka` instance runs on Yggdrasil. Entry points: `cargo check --workspace`, `crates/huginn-mind/src/admission.rs`, `crates/huginn-daemon/src/serve.rs`, `schemas/cultnet/`. Planned: what each crate does next is owned by the `eureka-body` cut specs in the mind and `docs/eureka-body-map.md`; the legacy `.voidbot/` Persona migration belongs to the portable-Persona work.
## Authority
- An instance owns its mind; Huginn owns the state. Huginn is the single
writer of an instance's memory documents. No other service, script, or agent
writes them.
- Memory documents are CultCache state. `huginn-mind` persists them through
CultLib's Rust CultCache into a redb store at
`<state_root>/minds/<instance>/mind.redb`. `huginn-daemon` serves one such
mind over CultNet RUDP: admission and the read side ride
`cultnet.operation_request.v0` and `cultnet.operation_response.v0`, and the
two wire schemas answer a schema catalog request.
- Retrieval depends on Qdrant directly, with no second store and no second
writer. `huginn-daemon` keeps one collection per mind, `huginn_mind_<instance>`,
as a projection of the mind: one worker thread embeds each indexable
document through Ollama and writes its point, off the serving path, and a
restart compares the mind with the collection and writes what is missing. An
unreachable Qdrant or Ollama shows in `whoami` as the index's status and is
retried; admission never waits on it. The daemon requires `--qdrant-url`,
`--ollama-url` and `--embedding-model`; `--ollama-url` repeats for each GPU
host and `--reserve-ollama-url` for each CPU host, and the GPU hosts embed
together while the CPU hosts serve only when none can. A `query` carrying `semantic` is
answered through the same selection as any other: the index supplies
candidate ids and scores, and the mind keeps only the documents it holds and
the selection admits (ask for `in_force` to exclude resolved ones), ordered by
score. It takes no cursor, and it is refused `Unavailable` while the index is
failing or refused.
- Mind state is not version-controlled. It lives in Huginn's store, not in
any repository.
Generic `.cc` inspection is not Huginn's job. CultCache Studio inspects and
edits `.cc` state. Huginn reads and writes minds.
## Layout
A Cargo workspace of five crates (`crates/eureka-pipeline` holds the document shapes and keys, `crates/mind-body` the control store, launch primitive and `mind-heartbeat` timeline; the three below are the mind, the daemon and the client):
- `crates/huginn-mind`: storage, identity, admission, and queries and derived
status over memory documents.
- `crates/huginn-daemon`: the CultNet surface. The socket, the sessions and
the process, and no rule. The operation envelope both sides speak is
`huginn-mind`'s.
- `crates/eureka-state`: the client core, `HuginnClient::call`: one request to a
daemon over CultNet RUDP, a deferred answer resolved, and no state. Its
`huginn` binary is the agents' CLI: whoami, view, query and named recipes,
admit with read-back, schema.
`huginn-mind` is live: it opens one instance's store (an owned redb CultCache
at `<state_root>/minds/<instance>/mind.redb`, locked for the mind's lifetime),
and refuses a store whose `instance` document names another instance, whose
epoch record is foreign, or whose types are not a mind's. It admits batches
of pipeline documents through one commit path: the leaf's bounds, formats and
keys, then the organ's cross-document rules (references, in-force status,
the resolution matrix, derived resolutions and stewardships), then one
compare-and-swap that lands the batch whole with a receipt naming the exact
bytes it read and wrote. An exact replay answers with the stored receipt.
It reads them back the same way: a document with the facts of its admission
joined from that receipt and its status derived at read time, and typed
queries over one mind. Status is never stored, and the views derive it
through the same rules admission does. Document shape and keys come from
`eureka-pipeline` (`crates/eureka-pipeline`); the store is CultLib's Rust CultCache.
`huginn-daemon` opens one mind, binds one UDP socket in that order, and
answers every frame on the session it arrived on: one operation per `Mind`
method plus `whoami`, whose payload is the mind's own types as named
MessagePack. A refusal is an answer with a `rejected` status, never a
transport error; an envelope that does not decode is answered with a typed
failure and reaches no mind. The two schemas it publishes live in
`schemas/cultnet/`, listed in its `index.json`, and are pinned to their
derivation by a test.
The campaign's cut specs in the mind (`eureka-body`) and
`docs/eureka-body-map.md` own what each crate does next.
```powershell
cargo check --workspace
cargo run -p huginn-daemon -- --state-root <abs> --instance <slug> --bind 127.0.0.1:17872 `
--qdrant-url http://127.0.0.1:6333 --ollama-url http://10.77.0.4:11434 --embedding-model qwen3-embedding:0.6b
```
The deployed daemon (instance `eureka` on Yggdrasil) is installed and run by
`gamecult-ops/runbooks/huginn-yggdrasil.md`.
## Persona
Huginn's legacy repo Persona lives under `.voidbot/`. Its `state/huginn.cc`
holds legacy `void.*` documents and its `voice/identity.json` names the
Persona. The two disagree about jurisdiction; that migration belongs to the
portable-Persona work, not to this workspace, and nothing here reads or writes
`.voidbot/`.