# ikigai-dev-server
A standalone, **linkage-gated** [ikigai](https://github.com/ikigai-rs) 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
| `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):
```toml
socket = "~/.ikigai/dev.sock" # this IS the default
# One line per root; the URN repo name is the directory's basename.
browse.root = "~/git-personal/ikigai-core"
browse.root = "~/git-personal/ikigai-cli"
# Optional once a root exists:
browse.store = "~/.ikigai/browse-store" # this IS the default
browse.file_model = "coder" # urn:llm:coder:ask
browse.dir_model = "ask" # urn:llm:ask
browse.file_max_tokens = 400
browse.dir_max_tokens = 600
```
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`):
```toml
mount = "prefer urn:repo:=~/.ikigai/dev.sock"
mount = "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`](https://github.com/ikigai-rs/ikigai-throttle): 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.