fix(tasks): correct Phase 2 task 1 and 2 to match actual codebase state

Task 1 (stream-interface-message-interface-split):
- Document that TransportKind::Dns EXISTS and must be removed (was incorrectly described as 'never added')
- Document that TransportKind::WebTransport has { host: String } and must be changed to { server_name: Option<String> }
- Document that ListenerConfig is a flat struct, not an enum, and must be restructured per ADR-035
- Move ListenerConfig restructuring, InterfaceConfig rename, and TransportKind cleanup into task 1 to avoid overlap with task 2
- Add HttpListenerConfig/DnsListenerConfig/StreamInterfaceKind/MessageInterfaceKind to task 1 scope

Task 2 (listenconfig-http-dns-stubs):
- Remove work now covered by task 1 (InterfaceConfig rename, TransportKind changes, ListenerConfig enum creation)
- Focus on wiring the new enum form into Server/ServeOptions/StaticConfig, adding constructors, validation, and accept loop stubs
This commit is contained in:
2026-06-09 10:00:23 +00:00
parent aafee72f4c
commit d7538a7806
2 changed files with 77 additions and 53 deletions

View File

@@ -1,6 +1,6 @@
---
id: listenconfig-http-dns-stubs
name: Update ListenerConfig with Http/Dns variants, add TransportKind::WebTransport tag, restructure InterfaceConfig
name: Add HttpListenerConfig/DnsListenerConfig wiring, StreamInterfaceKind/MessageInterfaceKind, and ListenerConfig enum helper methods
status: pending
depends_on: [stream-interface-message-interface-split]
scope: narrow
@@ -11,66 +11,68 @@ level: implementation
## Description
Add `ListenerConfig::Http` and `ListenerConfig::Dns` variants for message-based interfaces, add `TransportKind::WebTransport` as a tag-only variant, and restructure `InterfaceConfig` into `StreamInterfaceConfig` and `MessageInterfaceConfig` to align with the `StreamInterface`/`MessageInterface` split.
After the `stream-interface-message-interface-split` task restructures `ListenerConfig` from a flat struct to the ADR-035 enum form (with `Stream`, `Http`, `Dns` variants), removes `TransportKind::Dns`, and adds the `StreamInterfaceConfig`/`MessageInterfaceConfig`/`StreamInterfaceKind`/`MessageInterfaceKind`/`HttpListenerConfig`/`DnsListenerConfig` types, this task wires those new types into the server's accept loop and adds the helper methods, validation, and constructors that make the new `ListenerConfig` enum usable.
Per the integration plan section 2.5 and research/phase2/interface-model.md and tls-transport.md:
**Current state**:
- `ListenerConfig` likely has a single variant or is not yet fully defined (Phase 1 added `TransportKind` variants and `InterfaceConfig` but the `ListenerConfig` may need updating)
- `TransportKind` has `Tcp`, `Tls`, `Iroh` — no `Dns` (correctly), no `WebTransport`
- `InterfaceConfig` has `Ssh(SshInterfaceConfig)` and `RawFraming(RawFramingConfig)` — needs restructuring to `StreamInterfaceConfig`
**What task 1 (stream-interface-message-interface-split) already did**:
- `ListenerConfig` restructured to enum with `Stream { transport, interface }`, `Http { config: HttpListenerConfig }`, `Dns { config: DnsListenerConfig }` variants
- `TransportKind::Dns` removed
- `TransportKind::WebTransport` updated to `{ server_name: Option<String> }`
- `StreamInterfaceConfig`/`MessageInterfaceConfig` enums defined
- `StreamInterfaceKind`/`MessageInterfaceKind` enums defined
- `HttpListenerConfig` and `DnsListenerConfig` struct types defined
- `is_valid_pair()` updated for StreamInterface pairs only
**Key changes**:
- Add `TransportKind::WebTransport` variant — tag-only, no acceptor implementation. This is a trivial addition that prevents a breaking change later when WebTransport lands in Phase 5.
- Confirm `TransportKind::Dns` is NOT in the enum (DNS is a `MessageInterface`, not a transport). If it somehow got added, remove it. (Research confirms it was never added.)
- Rename `InterfaceConfig``StreamInterfaceConfig` (aligned with the trait rename from task 1)
- Add `StreamInterfaceConfig::Ssh` and `StreamInterfaceConfig::RawFraming` variants
- Add `MessageInterfaceConfig` enum with `Http` and `Dns` variants (and their config structs)
- Add `HttpListenerConfig` struct: `bind_addr`, `tls: bool`, `stealth: bool`
- Add `DnsListenerConfig` struct: `bind_addr`, `tls: bool`
- Update `ListenerConfig` to have three variants:
- `Stream { transport: TransportKind, interface: StreamInterfaceKind }` (existing pattern, renamed)
- `Http { config: HttpListenerConfig }`
- `Dns { config: DnsListenerConfig }`
- `TransportKind::WebTransport` is a tag-only enum variant — no `WebTransportAcceptor` implementation, no feature flag, just the variant existing so that config parsing can reference it
**What this task adds** (the wiring layer on top):
**Note on stealth mode**: The `Http` variant's `stealth` field means "if true, do byte-peek protocol detection on incoming TLS connections". This connects to the existing `stealth.rs` protocol detection. The axum router scaffold (task 2.7) handles the routing when stealth mode detects HTTP traffic. This task just defines the config types.
- **ListenerConfig constructors and helpers**: The enum exists but needs `tcp()`, `tls()`, `iroh()` convenience constructors that produce `ListenerConfig::Stream`, and `http()` / `dns()` constructors that produce their respective variants. These replace the old struct-style builders that task 1 removed.
- **ListenerConfig validation**: `validate()` method on the enum that checks: TLS cert/key requirements for Stream+Tls listeners, stealth-only-on-TLS for Http listeners, no TLS options on non-TLS variants.
- **Server accept loop wiring**: Update `Server::run()` and `handle_connection()` to match on the `ListenerConfig` enum variants. The `Stream` variant runs through the existing SSH/raw-framing accept path. The `Http` and `Dns` variants are stubs for now (Http defers to task 2.7 for the axum router, Dns defers to Phase 5).
- **Display implementations**: Ensure all new types have proper Display impls (task 1 likely added basic ones; add any missing).
- **ServeOptions integration**: Update `ServeOptions` to work with the new `ListenerConfig` enum — the `listeners` field should accept the enum form, and `StaticConfig::from_serve_options()` should produce `ListenerConfig` enum values.
**Note on stealth mode**: The `HttpListenerConfig.stealth` field means "if true, do byte-peek protocol detection on incoming TLS connections". This connects to the existing `stealth.rs` protocol detection. The axum router scaffold (task 2.7) handles the routing when stealth mode detects HTTP traffic. This task just wires the config types into the server.
## Acceptance Criteria
- [ ] `TransportKind::WebTransport { server_name: Option<String> }` variant added as tag-only (no acceptor impl, compiles but has no effect on server behavior)
- [ ] `TransportKind::Dns` confirmed absent from the enum (DNS is a `MessageInterface`, not a transport)
- [ ] `InterfaceConfig` renamed to `StreamInterfaceConfig` (or `StreamConfig` — aligned with the trait rename) with `Ssh` and `RawFraming` variants
- [ ] `MessageInterfaceConfig` enum added with `Http` and `Dns` variants
- [ ] `HttpListenerConfig` struct defined with `bind_addr: SocketAddr`, `tls: bool`, `stealth: bool`
- [ ] `DnsListenerConfig` struct defined with `bind_addr: SocketAddr`, `tls: bool`
- [ ] `ListenerConfig` enum has three variants: `Stream { transport, interface }`, `Http { config: HttpListenerConfig }`, `Dns { config: DnsListenerConfig }`
- [ ] `StreamInterfaceKind` enum defined (corresponding to `StreamInterface` implementors: `Ssh`, `RawFraming`)
- [ ] `MessageInterfaceKind` enum defined (corresponding to `MessageInterface` implementors: `Http`, `Dns`)
- [ ] `is_valid_pair()` validation updated for `Stream` listener configs (only valid Transport/StreamInterface combos allowed)
- [ ] `Display` implementations added for all new enums
- [ ] Serialization support (`serde::Serialize`/`Deserialize`) for all new config types
- [ ] All existing server/transport tests pass unchanged
- [ ] Unit test: `TransportKind::WebTransport` variant exists and can be constructed
- [ ] `ListenerConfig` enum has convenience constructors: `tcp(addr)``ListenerConfig::Stream`, `tls(addr)``ListenerConfig::Stream`, `iroh(addr)``ListenerConfig::Stream`, `http(config)``ListenerConfig::Http`, `dns(config)``ListenerConfig::Dns`
- [ ] `HttpListenerConfig` has a builder-pattern API: `HttpListenerConfig::new(addr).tls(true).stealth(true)`
- [ ] `DnsListenerConfig` has a builder-pattern API: `DnsListenerConfig::new(addr).tls(true)`
- [ ] `ListenerConfig::validate()` works for all three variants: Stream checks TLS cert/key, Http checks stealth-only-with-TLS, Dns has minimal validation
- [ ] `Server::run()` updated to match on `ListenerConfig` variants: Stream variant uses existing accept path, Http/Dns variants are stubs that log "not yet implemented" for now
- [ ] `StaticConfig::from_serve_options()` produces `ListenerConfig` enum values correctly
- [ ] `ServeOptions.listeners` field works with the new enum form
- [ ] `is_valid_pair()` called during `ListenerConfig::Stream` validation
- [ ] Serialization support (`serde::Serialize`/`Deserialize`) for all config types verified working
- [ ] All existing server/transport tests pass (updated to use new enum constructors)
- [ ] Unit test: `ListenerConfig::Http` variant constructs with `HttpListenerConfig`
- [ ] Unit test: `ListenerConfig::Dns` variant constructs with `DnsListenerConfig`
- [ ] Unit test: `ListenerConfig::Stream` validates TLS cert/key requirements
- [ ] Unit test: stealth on non-TLS Http listener is rejected by validation
## References
- docs/research/integration-plan.md — Phase 2.5
- docs/research/phase2/interface-model.md — ListenerConfig, TransportKind, InterfaceKind redesign
- docs/research/phase2/tls-transport.md — HTTP listener config, stealth mode
- crates/alknet-core/src/interface/config.rs — Current InterfaceConfig, InterfaceKind
- docs/architecture/decisions/035-streaminterface-messageinterface-split.md — ADR-035 (ListenerConfig enum form)
- crates/alknet-core/src/interface/config.rs — InterfaceConfig (now StreamInterfaceConfig/MessageInterfaceConfig)
- crates/alknet-core/src/interface/pairs.rs — Valid transport-interface pairs
- crates/alknet-core/src/server/serve.rs — ListenerConfig, Server, ServeOptions, StaticConfig
## Notes
> Use `#[non_exhaustive]` on `ListenerConfig`, `StreamInterfaceKind`, `MessageInterfaceKind`, and `MessageInterfaceConfig` so future variants (WebSocket, gRPC) don't break downstream.
> This task depends heavily on what task 1 produces. Before starting, do `git fetch origin && git merge origin/main --no-edit` to get task 1's changes, then read the current state of `ListenerConfig`, `Server`, `ServeOptions`, and `StaticConfig` to understand the enum form they now take.
> The `Http` and `Dns` accept loop stubs should be minimal — just log a message and skip the connection. The full implementations come in task 2.7 (axum scaffold) and Phase 5 (DNS).
> The `stealth` field on `HttpListenerConfig` controls whether the server does byte-peek protocol detection (first bytes → SSH vs HTTP). When `stealth: true` on a listener sharing port 443 with SSH, the accept loop routes based on protocol detection. When `stealth: false`, the HTTP listener receives all traffic directly.
> The `tls: bool` field is separate from `stealth`. `tls: true` means "use TLS on this listener". `stealth: true` means "peek first bytes to detect SSH vs HTTP". These are orthogonal: you can have TLS + stealth (port 443), TLS without stealth (port 8443), plain HTTP without stealth (port 8080), etc.
> Use `#[non_exhaustive]` on `ListenerConfig`, `StreamInterfaceKind`, `MessageInterfaceKind`, and `MessageInterfaceConfig` so future variants (WebSocket, gRPC) don't break downstream.
## Summary
> To be filled on completion

View File

@@ -1,6 +1,6 @@
---
id: stream-interface-message-interface-split
name: Rename Interface → StreamInterface, add MessageInterface trait and restructure config types
name: Rename Interface → StreamInterface, add MessageInterface trait, remove TransportKind::Dns, restructure ListenerConfig
status: pending
depends_on: []
scope: moderate
@@ -11,7 +11,7 @@ level: implementation
## Description
Rename the current `Interface` trait to `StreamInterface` and add a `MessageInterface` trait for request/response interfaces (HTTP, DNS). This is the most impactful structural change in Phase 2 because all subsequent tasks reference the new trait names and config types.
Rename the current `Interface` trait to `StreamInterface`, add a `MessageInterface` trait for request/response interfaces (HTTP, DNS), remove `TransportKind::Dns` from the transport enum (DNS is a `MessageInterface`, not a transport), restructure `ListenerConfig` from a flat struct to the ADR-035 enum form, and align `TransportKind::WebTransport` with the spec. This is the most impactful structural change in Phase 2 because all subsequent tasks reference the new trait names and config types.
Per ADR-035 and research/phase2/interface-model.md:
@@ -22,38 +22,51 @@ Per ADR-035 and research/phase2/interface-model.md:
- Add `HttpInterface` stub (struct definition, no route implementations yet)
- Add `DnsInterface` stub (struct definition only)
- Restructure `InterfaceConfig` into `StreamInterfaceConfig` and `MessageInterfaceConfig`
- Update `ListenerConfig` to include `Stream`, `Http`, and `Dns` variants per the research docs
- Add `TransportKind::WebTransport` as a tag-only variant (no acceptor implementation)
- Remove any references to `TransportKind::Dns` (it was never added, so no removal needed — just ensure it's not added)
- Restructure `ListenerConfig` from its current flat struct form to the ADR-035 enum with `Stream`, `Http`, and `Dns` variants
- **Remove `TransportKind::Dns`** — DNS is a `MessageInterface`, not a transport. This is a residual from Phase 1 that needs cleanup. All code referencing it must be removed: the `ListenerConfig::dns()` constructor, `pairs.rs` valid pairs table, `TransportKindBase::Dns`, and all related tests.
- **Update `TransportKind::WebTransport`** field from `{ host: String }` to `{ server_name: Option<String> }` per ADR-035 (tag-only variant, `server_name` is optional)
**Why this must go first**: Every other Phase 2 task imports and references these types. The rename and new traits must land before SshSession bridge, RawFraming implementation, or HTTP scaffold work begins. The integration plan section 2.3 explicitly states: "This task should be done early in Phase 2 because all subsequent tasks reference the new trait names."
**Current state of the code**:
**Current state of the code** (IMPORTANT — these differ from what the research docs assumed):
- `Interface` trait in `crates/alknet-core/src/interface/mod.rs` with `accept()``Self::Session`
- `InterfaceSession` trait in `crates/alknet-core/src/interface/session.rs` with `recv()` / `send()`
- `InterfaceConfig` enum with `Ssh(SshInterfaceConfig)` and `RawFraming(RawFramingConfig)` variants
- `InterfaceKind` enum with `Ssh` and `RawFraming` variants
- `TransportKind` has `Tcp`, `Tls`, `Iroh` (no `Dns`)
- `TransportKind` has `Tcp`, `Tls { server_name: Option<String> }`, `Iroh { endpoint_id: String }`, `Dns { domain: String }`, and `WebTransport { host: String }`
- **`TransportKind::Dns` exists in the current code** — it must be removed. References exist in `pairs.rs` (`TransportKindBase::Dns`), `serve.rs` (`ListenerConfig::dns()` constructor, Display impl, validate logic), `transport/mod.rs` (Display impl, tests), and `lib.rs` re-exports.
- **`TransportKind::WebTransport` exists but has `{ host: String }`** — must be changed to `{ server_name: Option<String> }` per ADR-035.
- `ListenerConfig` is currently a **flat struct** (fields: `transport_kind`, `interface_kind`, `listen_addr`, `tls_cert`, `tls_key`, `acme_domain`, `stealth`, `iroh_relay`) with builder-pattern constructors (`tcp()`, `tls()`, `iroh()`, `dns()`, `webtransport()`) — NOT an enum. Must be restructured to the ADR-035 enum form.
- `SshInterface` implements `Interface`, `SshSession` implements `InterfaceSession`
- `RawFramingInterface`/`RawFramingSession` are stubs (Phase 1 left them)
## Acceptance Criteria
- [ ] `Interface` trait renamed to `StreamInterface` throughout alknet-core (mod.rs, ssh.rs, raw_framing.rs, and all import sites)
- [ ] `Interface` trait renamed to `StreamInterface` throughout alknet-core (mod.rs, ssh.rs, raw_framing.rs, and all import sites including `lib.rs`)
- [ ] `MessageInterface` trait defined in `crates/alknet-core/src/interface/mod.rs` with `handle_request(&self, request: InterfaceRequest) -> Result<InterfaceResponse>`
- [ ] `InterfaceRequest` struct defined with `operation_path`, `input`, `auth_token`, `metadata` fields per interface-model.md
- [ ] `InterfaceResponse` struct defined with `result`, `status`, `headers` fields per interface-model.md
- [ ] `HttpInterface` stub struct defined (identity_provider, registry, env fields) — no route implementations
- [ ] `DnsInterface` stub struct defined (domain, identity_provider, registry, env fields) — no implementation
- [ ] `InterfaceConfig` restructured: `StreamInterfaceConfig::Ssh` and `StreamInterfaceConfig::RawFraming` replace current variants; `MessageInterfaceConfig` enum added with `Http` and `Dns` variants (or the variant types are defined)
- [ ] `ListenerConfig` enum updated with `Stream { transport, interface }`, `Http { bind_addr, tls, stealth }`, and `Dns { bind_addr, tls }` variants
- [ ] `TransportKind::WebTransport` added as tag-only variant (no acceptor, no feature flag beyond the enum variant)
- [ ] `is_valid_pair()` / `TransportKindBase` validation updated for StreamInterface pairs only
- [ ] All existing tests pass after the rename (SshInterface and RawFramingInterface still compile and pass)
- [ ] `InterfaceConfig` restructured: `StreamInterfaceConfig::Ssh` and `StreamInterfaceConfig::RawFraming` replace current variants; `MessageInterfaceConfig` enum added with `Http` and `Dns` variants
- [ ] `ListenerConfig` restructured from flat struct to enum with `Stream { transport: TransportKind, interface: StreamInterfaceKind }`, `Http { config: HttpListenerConfig }`, and `Dns { config: DnsListenerConfig }` variants per ADR-035
- [ ] `StreamInterfaceKind` enum defined (corresponding to `StreamInterface` implementors: `Ssh`, `RawFraming`)
- [ ] `MessageInterfaceKind` enum defined (corresponding to `MessageInterface` implementors: `Http`, `Dns`)
- [ ] `TransportKind::Dns` **removed** from the enum and all references cleaned up: `TransportKindBase::Dns` in pairs.rs, `ListenerConfig::dns()` constructor, Display impls, validate logic, and all related tests
- [ ] `TransportKind::WebTransport` field changed from `{ host: String }` to `{ server_name: Option<String> }` and all references updated
- [ ] `is_valid_pair()` / `TransportKindBase` validation updated for StreamInterface pairs only (no DNS pairs)
- [ ] All existing tests pass after the changes (SshInterface and RawFramingInterface still compile and pass)
- [ ] New `StreamInterface` implementors still use `InterfaceSession` for `type Session`
- [ ] `MessageInterface` has at least one compilation test (e.g., a mock struct implements it)
- [ ] `HttpInterface` and `DnsInterface` stubs compile and exist in the type system
- [ ] `lib.rs` re-exports all new types (`StreamInterface`, `MessageInterface`, `InterfaceRequest`, `InterfaceResponse`, `HttpInterface`, `DnsInterface`)
- [ ] `lib.rs` re-exports all new types (`StreamInterface`, `MessageInterface`, `InterfaceRequest`, `InterfaceResponse`, `HttpInterface`, `DnsInterface`, `StreamInterfaceConfig`, `MessageInterfaceConfig`, `StreamInterfaceKind`, `MessageInterfaceKind`, `HttpListenerConfig`, `DnsListenerConfig`)
- [ ] `HttpListenerConfig` struct defined with `bind_addr: SocketAddr`, `tls: bool`, `stealth: bool`
- [ ] `DnsListenerConfig` struct defined with `bind_addr: SocketAddr`, `tls: bool`
- [ ] `Display` implementations added for all new enums/structs
- [ ] `#[non_exhaustive]` on `ListenerConfig`, `StreamInterfaceKind`, `MessageInterfaceKind`, `MessageInterfaceConfig`
- [ ] Serde `Serialize`/`Deserialize` on all new config types
- [ ] Updated tests cover the new `ListenerConfig` enum form, the removed `Dns` transport, and the `WebTransport` field rename
## References
@@ -63,15 +76,24 @@ Per ADR-035 and research/phase2/interface-model.md:
- crates/alknet-core/src/interface/mod.rs — Current Interface trait
- crates/alknet-core/src/interface/session.rs — InterfaceSession, InterfaceEvent
- crates/alknet-core/src/interface/config.rs — Current InterfaceConfig
- crates/alknet-core/src/interface/pairs.rs — Valid transport-interface pairs
- crates/alknet-core/src/interface/pairs.rs — Valid transport-interface pairs (has TransportKindBase::Dns to remove)
- crates/alknet-core/src/transport/mod.rs — TransportKind enum (has Dns and WebTransport { host } to fix)
- crates/alknet-core/src/server/serve.rs — ListenerConfig flat struct (to restructure to enum)
- crates/alknet-core/src/lib.rs — Re-exports
## Notes
> This is the most mechanically invasive change in Phase 2 due to the rename, but it's low-risk behaviorally. The `Interface` → `StreamInterface` rename is a find-and-replace operation. The new `MessageInterface` trait and stubs are purely additive. The `ListenerConfig` restructuring is additive since no existing code uses `ListenerConfig::Http` or `ListenerConfig::Dns` yet.
> This is the most mechanically invasive change in Phase 2 due to the rename and the `ListenerConfig` restructuring, but it's low-risk behaviorally. The `Interface` → `StreamInterface` rename is a find-and-replace operation. The new `MessageInterface` trait and stubs are purely additive. The `ListenerConfig` restructuring is the biggest change — it's currently a flat struct with builder-pattern constructors, and needs to become an enum with `Stream`, `Http`, and `Dns` variants per ADR-035. The `Server::new()` code and `StaticConfig::from_serve_options()` code that currently uses the struct form will need to be updated.
> The `TransportKind::Dns` removal is cleanup from Phase 1 — it was incorrectly added as a transport variant when DNS is actually a `MessageInterface`. All references must be removed: the `TransportKindBase::Dns` in `pairs.rs`, the `ListenerConfig::dns()` constructor in `serve.rs`, the `VALID_TRANSPORT_INTERFACE_PAIRS` table entry, and the Display/test code. The DNS functionality will be represented by `ListenerConfig::Dns { config: DnsListenerConfig }` instead.
> The `TransportKind::WebTransport` field change from `{ host: String }` to `{ server_name: Option<String> }` aligns with ADR-035. Since no code currently depends on the `host` field specifically (there's no WebTransport acceptor), this is a safe rename.
> The integration plan section 2.3 notes: "Existing `SshInterface` and `RawFramingInterface` become `StreamInterface` implementations. No behavior change for stream-based interfaces."
> Consider using `#[non_exhaustive]` on the new enums (`MessageInterfaceConfig`, `ListenerConfig`) so future variants (WebSocket, etc.) don't break downstream.
> Consider using `#[non_exhaustive]` on the new enums (`MessageInterfaceConfig`, `ListenerConfig`, `StreamInterfaceKind`, `MessageInterfaceKind`) so future variants (WebSocket, etc.) don't break downstream.
> When restructuring `ListenerConfig` from struct to enum, the `Server::new()`, `StaticConfig::from_serve_options()`, and `serve.rs` accept loop code will need updates. The `ServeOptions.listeners` field type changes. The `ListenerConfig::tcp()`, `tls()`, `iroh()` constructors should become associated functions that produce the `ListenerConfig::Stream` variant. New constructors for `Http` and `Dns` variants will be added.
## Summary