check-layering.sh)
fails the build on any upward import.
Canonical source:
docs/architecture/crate-layer-registry.md.
This page curates the layer model for readers; the authoritative manifest
lives in the engine repo next to the enforcement script.Mechanical sort test
Every new crate answers these questions in order. First hit wins.The pyramid
Arrows read βdepends onβ. Upward arrows are forbidden βcheck-layering.sh fails on any workspace-local dep that points up.
Layer table
| Layer | Role | Allowed I/O | Allowed deps | Example crates |
|---|---|---|---|---|
| L0 | Pure types, lookup tables, sync-only APIs | none | (leaf) | nika-types, nika-error, nika-catalog, nika-schema, nika-event, nika-binding, nika-transform, nika-pck-manifest, nika-catalog-codegen |
| L0.5 | Kernel trait definitions + companions (mock) β async OK | none (traits only) | L0 | nika-kernel (facade hub), nika-kernel-core, nika-kernel-ai, nika-kernel-runtime, nika-kernel-plugin, nika-kernel-mock |
| L1 | Effect implementations β async, per-crate capability axis | declared axes only (fs / net / exec / env) | L0, L0.5 | nika-fs, nika-http, nika-process, nika-git, nika-keys-*, nika-pck-registry, nika-pck-store, nika-<provider>-* |
| L2 | Verbs + domain services β orchestrates L1 impls behind kernel traits | via L1 traits only | L0, L0.5, L1 | nika-pck, nika-verb-*, nika-policy, nika-connectome, nika-observability, nika-builtin-{github,cloud,workspace} |
| L3 | Runtime + policy + sandbox β enforces execution contracts | via L2 | L0..L2 | nika-runtime, nika-shield, nika-wasm-host (v0.100), nika-sandbox (v0.100) |
| L4 | Interfaces β transport / UI surfaces (libraries) | via L3 | L0..L3 | nika-cli, nika-serve, nika-mcp, nika-lsp, nika-sdk, nika-catalog-verify |
| L5 | The binary β sole [[bin]] composition root | via L4 | L0..L4 | nika (<500 LOC) |
Admitted today (v)
nika-types
L0 Β· Foundation value types. Leaf of the DAG.
nika-error
L0 Β· NIKA-XXX error hierarchy +
miette integration.nika-catalog
L0 Β· Provider / capability TOML catalog, phf lookup.
nika-kernel
L0.5 Β· 40 ISP traits, sealed supertrait, prelude re-export hub.
nika-kernel-mock
L0.5 Β· Pure-memory trait mocks for testing.
nika-catalog-verify
L4 Β· Build-only catalog validation tool.
nika-schema (parser scaffolding, Round 4 admission gate).
Security axes
Every L1 crate declares the capabilities it exercises indocs/architecture/security-axes.toml. Reserving an axis costs nothing
today and forbids its silent later use.
The 12 capability axes
The 12 capability axes
| Axis | Meaning |
|---|---|
reads-env | Reads process environment variables |
reads-fs | Reads filesystem (read-only) |
rw-fs | Reads and writes filesystem |
exec-shell | Spawns subprocesses |
net-egress | Initiates outbound network connections |
net-ingress | Accepts inbound connections |
spawns-thread | Uses std::thread::spawn or tokio::task::spawn_blocking |
mutates-global | Mutates process-global state (env, CWD, signals) |
panics-allowed | Explicit opt-out from zero-panic discipline (rare) |
reads-secrets | Reads Secret<T> / SecretRef material |
time-mutation | Manipulates clocks (MockClock, tokio::time::pause) |
allocator-sensitive | Imposes allocator constraints (pre-flight for WASM v0.100) |
thread-local-state | Uses thread_local! / static RefCell (incompatible with WASM) |
How enforcement works
How enforcement works
Three CI vectors enforce layer discipline:
check-layering.sh(hygiene vector 11, P0, fail on violation): reads layer membership from[workspace.metadata.diamond.layers]and verifies every workspace-local dependency is equal or lower layer.check-security-axes.sh(vector 12, P1): cross-checks declared axes against static analysis of the source tree.check-no-async-in-l0.sh(vector 16, P1): grepsasync fn/async {in L0 source trees; fails on any match.
Forward compatibility
- The Connectome β 1 L2 orchestrator (
nika-connectome) + 10 L1 satellites (hnsw, bm25, rrf, rerank, fsrs, rdfs-reasoner, temporal, graph-algos, autodesc-minimal, autodesc-full). No renumber β they slot into L1 / L2 naturally. - v0.100 WASM plugin host + sandbox β L3 crates alongside
nika-runtime. No renumber.
Anti-patterns
- Kitchen-sink crate β βIβll just add this utility to
nika-errorβ leads to a 10k-LOC bag-of-tricks every crate transitively pulls in. One caller = stays in the caller. - Facade mega-crate β βRe-export everything from
nikafor convenienceβ collapses the layer contract and tanks compile time. - Upward re-exports β an L1 crate
pub use nika_pck::...turns its API into an L2 consumer and breaks mechanical sorting. asyncin L0 β forces a runtime choice on every L1 / L2 consumer; makes the crate unusable in build scripts.anyhow::Errorin library code β L5 binary only. L0..L4 uses the typed error hierarchy.Box<dyn Error>in public API β hygiene vector 19 bans it. Swap to the typed error hierarchy.- Runtime imports in L0 β
tokio::spawninnika-erroris a layer violation even if it compiles.
See also
- Forward-compat invariants β the 8 patterns + 5 decisions that protect the public API
- L0 foundation decisions β Q1 through Q13, locked
- 12-gate admission β how a crate earns a seat at the workspace
- Constellation β live map of admitted / WIP / planned crates