ikigai-dev-server
A standalone, linkage-gated ikigai server for
development tooling. Its Cargo.toml is the module manifest: the binary
composes a curated space and links only what it serves.
ikigai-dev [socket] [flags] # default: ~/.ikigai/dev.sock
ikigai --connect <socket> # drive it from the REPL
ikigai-dev --help # flags + the config-file grammar
What it serves
| resource | from |
|---|---|
urn:system:exec, urn:repo:* |
ikigai-repo — git/gh/cargo as capability-gated resources |
urn:rdf:* |
ikigai-rdf — graph union/diff/transrept |
urn:sparql:* |
ikigai-sparql — query |
urn:repo:{repo}:tree/file/state/hash/explain/…, urn:annotation:* |
ikigai-browse — the browse family, when configured (see below) |
urn:llm:* |
ikigai-llm — the explain seam's derivation engine, mounted only alongside browse |
Run it in a project directory and it serves that project's git state
(source urn:repo:status, :log, :branch), or pass dir= for another repo.
The browse family (opt-in)
This server owns the browse store on a machine (decision of record): the
persistent explanation/annotation archive is RocksDB under an exclusive lock,
so ONE process holds it and everyone else prefer-mounts this server's socket —
which is why the default socket is the stable ~/.ikigai/dev.sock rather than
something under $TMPDIR that churns across reboots.
Configure it in ~/.config/ikigai/dev.toml (flags override the file; there is
deliberately no environment-variable channel):
= "~/.ikigai/dev.sock" # this IS the default
# One line per root; the URN repo name is the directory's basename.
= "~/git-personal/ikigai-core"
= "~/git-personal/ikigai-cli"
# Optional once a root exists:
= "~/.ikigai/browse-store" # this IS the default
= "coder" # urn:llm:coder:ask
= "ask" # urn:llm:ask
= 400
= 600
= "coder" # review pass (urn:repo:{repo}:review:{path})
= 800 # findings carry quotes — raise for big files/PRs
= "coder" # pull-request explain (the PR family)
= 600
= "qwen3:30b" # operator overrides folded into version tags;
= "qwen3:30b" # unset, the true model id resolves at derive time
No browse.root ⇒ no browse family at all (and no urn:llm:*) — the original
curated surface, unchanged. A browse.* tuning without a root fails loud at
startup: half-configured must never come up looking healthy. With roots, the
store opens (or fails loud — never a silent in-memory archive), the bundled
vocabulary loads, and urn:sparql:* binds over the same store, so one
query joins ik:Explanation and oa:Annotation rows live.
Every other process on the machine reaches the family through this socket
(in the main host's ~/.config/ikigai/config.toml):
= "prefer urn:repo:=~/.ikigai/dev.sock"
= "prefer urn:annotation:=~/.ikigai/dev.sock"
The LLM provider registry is the shared ~/.config/ikigai/llm.json (absent ⇒
a local Ollama default; present-but-unparseable ⇒ loud).
Why linkage-gating
ikigai mcp --grant dev on the omnibus binary config-gates — it hides the
calendar tools, but their code is still linked and reachable in-process. This
binary doesn't compile them in at all: EventKit and the calendar are not
present, so a flaw in either cannot be reached. The security bound is the
dependency list, not a runtime check.
ikigai-llm is linked — deliberately, and only mounted when browse is
configured. The explain seam derives through urn:llm:*, and ikigai-llm is an
outbound HTTP client to local inference servers — not the EventKit/TCC class
of platform authority the gating posture exists to exclude. The rejected
alternative (mounting urn:llm: from the main host) would couple every fresh
explanation to the main host's uptime — the dev seam would degrade with the
very process it exists to stand apart from.
On top of that, the expensive seams are wrapped in a
RateLimit: 30 subprocess
spawns/min on exec, 120 reads/min on urn:repo:, and 30 asks/min on
urn:llm: (derivations are expensive; the limit governs explain's internal
asks too, since subrequests resolve back through the overlay). Pure local
graph ops pass unlimited.
Security posture (honest)
Three real bounds today — linkage (only these modules exist),
rate-limiting (exec and llm are capped), and the transport's peercred
check (owner-only socket). A per-scope capability ceiling enforced
server-side is the "capability-on-the-wire" work; until then the local owner
is trusted and the reachable surface is bounded by what is linked and limited.
Browse's own capability story (urn:cap:browse:read:*, urn:cap:annotate)
comes with the crate. An MCP face (so an agent connects under a grant) is a
fast-follow.
License
MIT OR Apache-2.0.