Summary
crates/iroh-http-core/src/endpoint.rs is 998 LoC, larger than the 747-LoC file the original epic-#182 evidence section called out as the symptom of structural drift. ADR-014 D1 prescribed a four-subsystem split (Transport, HttpRuntime, SessionRuntime, FfiBridge) as named modules with strict boundaries. Slice C.6 (commit b146b0c) inlined them all back into one file plus the configuration types from crate::config and crate::stats.
The result satisfies the canonical-root-layout invariant on paper but contradicts ADR-014 D1's "named subsystems with strict module boundaries." The four pub(crate) structs live as anonymous siblings inside one file, accessed via endpoint.inner.http.active_connections — there is no module boundary protecting any subsystem from any other.
This is the exact same drift pattern (named-but-not-delivered split) the epic body listed as the canonical example of what to prevent.
Evidence
endpoint.rs:1-13 header references the four ADR-014 D1 subsystems by name:
[IrohEndpoint] is a thin façade over [EndpointInner], which is composed of the four named subsystems from ADR-014 D1: [transport::Transport], [http_runtime::HttpRuntime], [session_runtime::SessionRuntime], [ffi_bridge::FfiBridge].
But those module paths don't exist. The structs are inline:
endpoint.rs:27-101:
pub(crate) struct Transport (L27)
pub(crate) struct HttpRuntime (L40)
pub(crate) struct SessionRuntime (L66) — with stale "Slice E carve-out" comment
pub(crate) struct FfiBridge (L96)
Plus 11 pub struct configuration / observability types (NetworkingOptions, DiscoveryOptions, PoolOptions, StreamingOptions, NodeOptions, NodeAddrInfo, EndpointStats, ConnectionEvent, PeerStats, PathInfo) and the IrohEndpoint facade itself.
Callers reach across subsystems freely. Example: crates/iroh-http-core/src/http/server/accept.rs:75-77 does endpoint.inner.http.active_connections directly. There is no encapsulation.
Impact
- ADR-014 D1 is partly aspirational. The names exist in the doc and the comment header; the boundaries don't. Anyone reading the ADR gets a misleading mental model.
- Future drift has no friction. A new field belongs in one of the four subsystems by ADR; in practice it lands wherever in the 998-line file is convenient. The "drift back to a god-object" pattern the epic was filed against is structurally available.
- The canonical-root-layout test, which is otherwise excellent, is currently doing extra work to prevent a problem that the named-modules split would have prevented more naturally. The test was filed to stop random files at root; allowlisting
endpoint/ as a folder doesn't reopen that vector.
Remediation
Promote the four subsystems to a folder:
crates/iroh-http-core/src/
├── lib.rs
├── endpoint/
│ ├── mod.rs — IrohEndpoint facade + EndpointInner only
│ ├── transport.rs — Transport struct + impls
│ ├── http_runtime.rs — HttpRuntime + ConnectionTracker
│ ├── session_runtime.rs — SessionRuntime + serve handle plumbing
│ ├── ffi_bridge.rs — FfiBridge + StoreConfig
│ ├── config.rs — NetworkingOptions, DiscoveryOptions, PoolOptions,
│ │ StreamingOptions, NodeOptions
│ └── stats.rs — EndpointStats, PeerStats, PathInfo,
│ ConnectionEvent, NodeAddrInfo
├── http/
└── ffi/
Update tests/architecture.rs::crate_root_has_canonical_layout ALLOWED to be &["lib.rs", "endpoint", "http", "ffi"] (folder, not file). The structural intent of ADR-014 D1 returns; the mod http / mod ffi invariant is unaffected.
Each subsystem pub(in crate::endpoint) struct so cross-subsystem reads must go through accessor methods on IrohEndpoint, not field access. That moves the encapsulation invariant from "trust review" to "trust the compiler."
If this is rejected, ADR-014 D1 should be edited to say "subsystems are inline structs in endpoint.rs" so the doc and the code agree. Status quo leaves them disagreeing, which is the whole pattern epic #182 was filed to prevent.
Acceptance criteria
- The four subsystems live in their own files under
endpoint/.
- Cross-subsystem field access is no longer possible — callers go through accessor methods on
IrohEndpoint (or the relevant subsystem facade).
tests/architecture.rs::crate_root_has_canonical_layout allowlists endpoint/ (a folder) and still rejects any other top-level entry under src/.
endpoint/mod.rs is ≤ 200 LoC (façade + module declarations only).
- ADR-014 D1 is updated if any names diverge from the prescription, or stays unchanged if the new structure matches it verbatim.
npm run ci green; 92 interop pairs pass.
References
Summary
crates/iroh-http-core/src/endpoint.rs is 998 LoC, larger than the 747-LoC file the original epic-#182 evidence section called out as the symptom of structural drift. ADR-014 D1 prescribed a four-subsystem split (
Transport,HttpRuntime,SessionRuntime,FfiBridge) as named modules with strict boundaries. Slice C.6 (commitb146b0c) inlined them all back into one file plus the configuration types fromcrate::configandcrate::stats.The result satisfies the canonical-root-layout invariant on paper but contradicts ADR-014 D1's "named subsystems with strict module boundaries." The four
pub(crate) structs live as anonymous siblings inside one file, accessed viaendpoint.inner.http.active_connections— there is no module boundary protecting any subsystem from any other.This is the exact same drift pattern (named-but-not-delivered split) the epic body listed as the canonical example of what to prevent.
Evidence
endpoint.rs:1-13 header references the four ADR-014 D1 subsystems by name:
But those module paths don't exist. The structs are inline:
endpoint.rs:27-101:
pub(crate) struct Transport(L27)pub(crate) struct HttpRuntime(L40)pub(crate) struct SessionRuntime(L66) — with stale "Slice E carve-out" commentpub(crate) struct FfiBridge(L96)Plus 11
pub structconfiguration / observability types (NetworkingOptions,DiscoveryOptions,PoolOptions,StreamingOptions,NodeOptions,NodeAddrInfo,EndpointStats,ConnectionEvent,PeerStats,PathInfo) and theIrohEndpointfacade itself.Callers reach across subsystems freely. Example: crates/iroh-http-core/src/http/server/accept.rs:75-77 does
endpoint.inner.http.active_connectionsdirectly. There is no encapsulation.Impact
endpoint/as a folder doesn't reopen that vector.Remediation
Promote the four subsystems to a folder:
Update tests/architecture.rs::crate_root_has_canonical_layout
ALLOWEDto be&["lib.rs", "endpoint", "http", "ffi"](folder, not file). The structural intent of ADR-014 D1 returns; themod http/mod ffiinvariant is unaffected.Each subsystem
pub(in crate::endpoint) structso cross-subsystem reads must go through accessor methods onIrohEndpoint, not field access. That moves the encapsulation invariant from "trust review" to "trust the compiler."If this is rejected, ADR-014 D1 should be edited to say "subsystems are inline structs in
endpoint.rs" so the doc and the code agree. Status quo leaves them disagreeing, which is the whole pattern epic #182 was filed to prevent.Acceptance criteria
endpoint/.IrohEndpoint(or the relevant subsystem facade).tests/architecture.rs::crate_root_has_canonical_layoutallowlistsendpoint/(a folder) and still rejects any other top-level entry undersrc/.endpoint/mod.rsis ≤ 200 LoC (façade + module declarations only).npm run cigreen; 92 interop pairs pass.References
endpoint.rs(then 747 LoC) as canonical drift evidence.b146b0c— the inlining that produced the current shape.