Skip to main content
Nika Diamond is crates organized into six layers, with a half-layer (L0.5) for trait-only crates. Every workspace-local dependency points strictly downward. A CI script (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.
1

Does it produce the `nika` binary?

β†’ L5 (the binary)
2

Does it expose a transport / UI surface (CLI, HTTP, MCP, LSP, SDK)?

β†’ L4 (interfaces)
3

Does it enforce runtime policy, sandboxing, or orchestration?

β†’ L3 (runtime + policy + sandbox)
4

Does it implement a verb or a domain service?

β†’ L2 (verbs + services)
5

Is it a primitive with I/O (fs, net, exec, env)?

β†’ L1 (effect impls)
6

None of the above?

β†’ L0 (pure, sync, zero I/O). If it’s trait-only but async is OK, β†’ L0.5.

The pyramid

Arrows read β€œdepends on”. Upward arrows are forbidden β€” check-layering.sh fails on any workspace-local dep that points up.

Layer table

LayerRoleAllowed I/OAllowed depsExample crates
L0Pure types, lookup tables, sync-only APIsnone(leaf)nika-types, nika-error, nika-catalog, nika-schema, nika-event, nika-binding, nika-transform, nika-pck-manifest, nika-catalog-codegen
L0.5Kernel trait definitions + companions (mock) β€” async OKnone (traits only)L0nika-kernel (facade hub), nika-kernel-core, nika-kernel-ai, nika-kernel-runtime, nika-kernel-plugin, nika-kernel-mock
L1Effect implementations β€” async, per-crate capability axisdeclared axes only (fs / net / exec / env)L0, L0.5nika-fs, nika-http, nika-process, nika-git, nika-keys-*, nika-pck-registry, nika-pck-store, nika-<provider>-*
L2Verbs + domain services β€” orchestrates L1 impls behind kernel traitsvia L1 traits onlyL0, L0.5, L1nika-pck, nika-verb-*, nika-policy, nika-connectome, nika-observability, nika-builtin-{github,cloud,workspace}
L3Runtime + policy + sandbox β€” enforces execution contractsvia L2L0..L2nika-runtime, nika-shield, nika-wasm-host (v0.100), nika-sandbox (v0.100)
L4Interfaces β€” transport / UI surfaces (libraries)via L3L0..L3nika-cli, nika-serve, nika-mcp, nika-lsp, nika-sdk, nika-catalog-verify
L5The binary β€” sole [[bin]] composition rootvia L4L0..L4nika (<500 LOC)
nika-http is the L1 HTTP client (used by fetch / catalog sync). The HTTP server surface of nika serve lives inline inside nika-serve L4 β€” no separate server crate.

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.
WIP: nika-schema (parser scaffolding, Round 4 admission gate).

Security axes

Every L1 crate declares the capabilities it exercises in docs/architecture/security-axes.toml. Reserving an axis costs nothing today and forbids its silent later use.
AxisMeaning
reads-envReads process environment variables
reads-fsReads filesystem (read-only)
rw-fsReads and writes filesystem
exec-shellSpawns subprocesses
net-egressInitiates outbound network connections
net-ingressAccepts inbound connections
spawns-threadUses std::thread::spawn or tokio::task::spawn_blocking
mutates-globalMutates process-global state (env, CWD, signals)
panics-allowedExplicit opt-out from zero-panic discipline (rare)
reads-secretsReads Secret<T> / SecretRef material
time-mutationManipulates clocks (MockClock, tokio::time::pause)
allocator-sensitiveImposes allocator constraints (pre-flight for WASM v0.100)
thread-local-stateUses thread_local! / static RefCell (incompatible with WASM)
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): greps async 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

Seven patterns that tank the layer contract. If you catch yourself writing one, stop and re-ask the mechanical sort test.
  1. 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.
  2. Facade mega-crate β€” β€œRe-export everything from nika for convenience” collapses the layer contract and tanks compile time.
  3. Upward re-exports β€” an L1 crate pub use nika_pck::... turns its API into an L2 consumer and breaks mechanical sorting.
  4. async in L0 β€” forces a runtime choice on every L1 / L2 consumer; makes the crate unusable in build scripts.
  5. anyhow::Error in library code β€” L5 binary only. L0..L4 uses the typed error hierarchy.
  6. Box<dyn Error> in public API β€” hygiene vector 19 bans it. Swap to the typed error hierarchy.
  7. Runtime imports in L0 β€” tokio::spawn in nika-error is a layer violation even if it compiles.

See also