ADR-014
Sealed kernel traits with explicit adapter registration
accepted · 2026-04-14 · L0.5 · cites 14
0ADR-014: Sealed kernel traits with explicit adapter registration #
Context #
nika-kernel (L0.5) defines ~20 public async traits — Provider, MemoryStore, ToolExecutor, FsRead, HttpGet, ShellExecutor, etc. Today only nika-kernel-mock implements them (L0.5 test). As Phase 2 admits L1 effect crates (nika-http, nika-fs, nika-process), each trait gains real implementors.
The open question: can an external crate (outside supernovae-st/nika workspace) implement these traits? If yes, every method signature becomes load-bearing across semver — adding a default-method parameter is a breaking change for external implementors. If no, we preserve flexibility to evolve method signatures as we learn.
2026 Rust practice (axum, serde, uv) uses the sealed-trait pattern (pub trait Foo: private::Sealed { ... }) to prevent external impls while keeping the trait public for users who depend on it via dyn Foo. rust-analyzer, rustls, tokio all seal core traits.
Decision #
All public traits in nika-kernel are sealed via the private::Sealed supertrait pattern:
// nika-kernel/src/lib.rsmod private { pub trait Sealed {}}// nika-kernel/src/provider.rspub trait ProviderInfer: private::Sealed + Send + Sync { /* ... */ }// nika-kernel/src/provider.rs (internal, for blanket Sealed impls)impl<T: crate::Provider + ?Sized> private::Sealed for T {}External crates cannot impl ProviderInfer for MyType (compile error: Sealed not implemented, and Sealed is private). Instead, external integrations route through:
- Adapter macros (
nika_kernel::provider_impl!) for workspace-local crates (nika-provider-rig,nika-provider-native,nika-provider-mock). - `nika-builtin-*` wrappers for ecosystem integrations (GitHub, Cloud, Workspace — ADR tbd).
- Future pck packages (plugin protocol, Phase 4+) for community extensions.
Adding a trait method still requires care, but the set of implementors is finite and controlled by the workspace.
Consequences #
Positive #
- Method signatures and default methods can evolve within v0.x without breaking external code (there isn't any external impl by construction).
- Clear path for ecosystem extensions (adapter macros > plugin protocol > pck) — enforced architecturally.
- Matches Rust 2026 canonical pattern.
Negative #
- Some friction for hypothetical out-of-workspace adapters — but there are none today, and when they emerge they belong as
nika-builtin-*or pck packages per ADR-004's crate-count discipline. - Adds a
private::Sealedmodule + blanket impl per trait group.
Neutral #
- Language-level
sealedkeyword RFC is still unmerged as of 2026-Q1 — the supertrait pattern is idiomatic.
Evidence #
- Pattern sources: axum, serde (
private::Sealed), rustls (server::ServerConfigsealed), tokio-stream crates/nika-kernel/src/lib.rs:19-34— trait hierarchy (to be updated with sealed pattern)
Alternatives considered #
Alt A — Open traits (no sealing) #
Rejected — every method change becomes potential breakage.
Alt B — Opaque impl trait only (no trait at all) #
Rejected — loses dyn Trait + breaks test substitutability (kernel-mock).
Alt C — `#[non_exhaustive]` on the trait #
Not valid in Rust — #[non_exhaustive] is structs/enums only.
Related #
- ADR-006 — kernel ISP traits (which this seals)
- ADR-012 — typestate runtime (uses sealed kernel via
bind_kernel(&dyn Kernel))
Notes #
Migration: sealing can be added to nika-kernel in a single commit before any L1 effect crate admits. No breaking change to nika-kernel-mock (workspace-local, blanket-Sealed applies). Plan for Phase 2 Session 1.
read at v0.107.0 · the decision record ships with the engine