Sync architecture specs with Phase 2 research findings
- Add definitions.md: normative terminology disambiguation (Interface, Service, Transport, Token, Identity, Domain, Scope, CredentialProvider, etc.) - Add credentials.md: CredentialProvider trait and CredentialSet enum for outbound auth, mirroring IdentityProvider pattern for inbound auth - Rewrite interface.md: StreamInterface/MessageInterface split (ADR-035), InterfaceRequest/InterfaceResponse, HttpInterface/DnsInterface stubs, ListenerConfig with Stream/Http/Dns variants, credential presentation table - Update auth.md: API keys in DynamicConfig (ADR-037), credential presentation per (Transport, Interface) pair, ApiKeyEntry struct in AuthPolicy - Update configuration.md: API keys, ListenerConfig with Http/Dns variants, expanded TOML config examples - Update call-protocol.md: resolve OQ-IF-01 (InterfaceEvent carries EventEnvelope + Identity), add MessageInterface awareness to protocol adapter layer - Update overview.md: three-layer model now includes StreamInterface/ MessageInterface, CredentialProvider/CredentialSet exports, definitions.md reference, ADRs 035-037 - Update open-questions.md: resolve OQ-IF-01, OQ-IF-02, add OQ-P2-01 through OQ-P2-04, add OQ-CP-01 through OQ-CP-04, add OQ-DEF-01, OQ-DEF-03, OQ-DEF-08 - Update README.md: add definitions.md, credentials.md, ADRs 035-037, phase2 research docs, current state description Key architectural decisions: - ADR-035: StreamInterface/MessageInterface split (two Layer 2 traits) - ADR-036: CredentialProvider as core type (outbound auth, alknet_core::credentials) - ADR-037: API keys as DynamicConfig auth (hash-verified bearer tokens)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-06-07
|
||||
last_updated: 2026-06-09
|
||||
---
|
||||
|
||||
# Interface (Layer 2)
|
||||
@@ -8,24 +8,33 @@ last_updated: 2026-06-07
|
||||
## What
|
||||
|
||||
The Interface layer sits between Transport (Layer 1) and Protocol (Layer 3).
|
||||
An Interface consumes a `Transport::Stream` and produces call protocol sessions.
|
||||
SSH is an interface, not a transport — it wraps a byte stream in session
|
||||
semantics. Raw framing (4-byte length prefix + JSON `EventEnvelope`) is another
|
||||
interface, one without SSH overhead.
|
||||
Interfaces consume byte streams from Transports or manage their own transports,
|
||||
and produce call protocol sessions or handle discrete requests. SSH is an
|
||||
interface, not a transport — it wraps a byte stream in session semantics. Raw
|
||||
framing (4-byte length prefix + JSON `EventEnvelope`) is another interface.
|
||||
HTTP and DNS are message-based interfaces that handle individual request/response
|
||||
pairs without persistent sessions.
|
||||
|
||||
## Why
|
||||
|
||||
In the current architecture, SSH is deeply embedded in `ServerHandler`. This
|
||||
tangling of transport, interface, and protocol makes it impossible to:
|
||||
In the original architecture, SSH was deeply embedded in `ServerHandler`. This
|
||||
tangling of transport, interface, and protocol made it impossible to:
|
||||
|
||||
- Run the call protocol over DNS queries without wrapping SSH inside DNS
|
||||
- Use raw framing for local service mesh (no SSH overhead)
|
||||
- Support WebTransport direct call protocol for browsers
|
||||
- Separate auth mechanics from channel management
|
||||
- Accept HTTP requests and map them to call protocol operations
|
||||
|
||||
The three-layer model (ADR-026) cleanly separates these concerns. Transport
|
||||
produces bytes. Interface parses bytes into sessions. Protocol carries
|
||||
semantics. A connection is always a (Transport, Interface) pair.
|
||||
produces bytes. Interface parses bytes into sessions or handles requests.
|
||||
Protocol carries semantics. A connection is always a (Transport, Interface)
|
||||
pair for stream-based interfaces, or a standalone message-based interface.
|
||||
|
||||
Phase 2 research identified that HTTP and DNS don't fit the persistent session
|
||||
model — they're stateless per-request. This led to the StreamInterface /
|
||||
MessageInterface split (ADR-035), which gives each interface category its own
|
||||
trait with the right lifecycle and ownership model.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -33,37 +42,103 @@ semantics. A connection is always a (Transport, Interface) pair.
|
||||
|
||||
```
|
||||
Layer 3: Protocol (Call protocol, Operations, OperationEnv)
|
||||
Layer 2: Interface (SSH, raw framing, HTTP/WS, DNS control channel)
|
||||
Layer 1: Transport (TCP, TLS, iroh, DNS, WebTransport)
|
||||
Layer 2: Interface (StreamInterface: SSH, raw framing | MessageInterface: HTTP, DNS)
|
||||
Layer 1: Transport (TCP, TLS, iroh, WebTransport)
|
||||
```
|
||||
|
||||
- **Layer 1: Transport** — produces byte streams (`AsyncRead + AsyncWrite + Unpin
|
||||
+ Send`). Unchanged per ADR-001.
|
||||
- **Layer 2: Interface** — consumes a `Transport::Stream` and produces call
|
||||
protocol sessions. SSH does handshake + auth + channel multiplexing. Raw
|
||||
framing does length-prefix parsing.
|
||||
+ Send`). Unchanged per ADR-001. DNS is NOT a transport.
|
||||
- **Layer 2: Interface** — two categories:
|
||||
- **StreamInterface**: consumes a `TransportStream` and produces a long-lived
|
||||
session that yields `InterfaceEvent` frames.
|
||||
- **MessageInterface**: handles individual `InterfaceRequest` →
|
||||
`InterfaceResponse` pairs. Manages its own transport.
|
||||
- **Layer 3: Protocol** — carries semantics. Call protocol events, operation
|
||||
registry, service calls. Agnostic to both Transport and Interface below it.
|
||||
|
||||
### Interface Trait
|
||||
### StreamInterface Trait
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait Interface: Send + Sync + 'static {
|
||||
type Session;
|
||||
async fn accept(stream: TransportStream, config: &InterfaceConfig) -> Result<Self::Session>;
|
||||
pub trait StreamInterface: Send + Sync + 'static {
|
||||
type Session: InterfaceSession;
|
||||
|
||||
async fn accept(
|
||||
&self,
|
||||
stream: Box<dyn TransportStream>,
|
||||
config: &InterfaceConfig,
|
||||
) -> Result<Self::Session>;
|
||||
}
|
||||
```
|
||||
|
||||
The session produced by an interface is consumed by the call protocol handler.
|
||||
Different interfaces produce different session types, but the call protocol
|
||||
handler receives `EventEnvelope` frames from any interface.
|
||||
The session produced by a `StreamInterface` is consumed by the call protocol
|
||||
handler. Different stream interfaces produce different session types, but the
|
||||
call protocol handler receives `InterfaceEvent` frames from any stream
|
||||
interface.
|
||||
|
||||
### SshInterface
|
||||
### MessageInterface Trait
|
||||
|
||||
Wraps the existing `ServerHandler` logic. This is the most complex interface
|
||||
because SSH provides channel multiplexing, auth negotiation, and proxy
|
||||
management within a single session.
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait MessageInterface: Send + Sync + 'static {
|
||||
async fn handle_request(&self, request: InterfaceRequest) -> Result<InterfaceResponse>;
|
||||
}
|
||||
```
|
||||
|
||||
Message-based interfaces handle individual requests without persistent sessions.
|
||||
They manage their own transport (HTTP server, DNS server) and normalize requests
|
||||
into `InterfaceRequest` / `InterfaceResponse`.
|
||||
|
||||
### InterfaceRequest / InterfaceResponse
|
||||
|
||||
```rust
|
||||
pub struct InterfaceRequest {
|
||||
pub operation_path: String, // e.g., "/head/auth/verify"
|
||||
pub input: Value, // JSON input payload
|
||||
pub auth_token: Option<AuthToken>, // Extracted from wire format
|
||||
pub metadata: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub struct InterfaceResponse {
|
||||
pub result: Result<Value, CallError>,
|
||||
pub status: u16, // HTTP status, DNS result code, etc.
|
||||
pub headers: HashMap<String, String>,
|
||||
}
|
||||
```
|
||||
|
||||
The call protocol handler processes `InterfaceRequest` the same way it processes
|
||||
`InterfaceEvent` frames — both resolve to operation invocations through
|
||||
`OperationEnv`. The difference is framing: stream interfaces produce `InterfaceEvent`
|
||||
frames from a continuous byte stream, message interfaces construct `InterfaceRequest`
|
||||
from their wire format.
|
||||
|
||||
### InterfaceSession
|
||||
|
||||
Every stream interface session implements `InterfaceSession`:
|
||||
|
||||
```rust
|
||||
pub struct InterfaceEvent {
|
||||
pub envelope: EventEnvelope,
|
||||
pub identity: Option<Identity>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait InterfaceSession: Send {
|
||||
async fn recv(&mut self) -> Option<InterfaceEvent>;
|
||||
async fn send(&mut self, envelope: EventEnvelope) -> Result<()>;
|
||||
}
|
||||
```
|
||||
|
||||
`InterfaceEvent` carries an `EventEnvelope` and the authenticated `Identity`.
|
||||
The call protocol handler (Layer 3) receives `InterfaceEvent` frames and
|
||||
processes them uniformly, regardless of whether they arrived over SSH or raw
|
||||
framing.
|
||||
|
||||
### SshInterface (StreamInterface)
|
||||
|
||||
Wraps the existing `ServerHandler` logic. This is the most complex stream
|
||||
interface because SSH provides channel multiplexing, auth negotiation, and
|
||||
proxy management within a single session.
|
||||
|
||||
What stays in SshInterface (Layer 2):
|
||||
- SSH handshake and session management
|
||||
@@ -79,7 +154,11 @@ What moves to Layer 3 (call protocol handler):
|
||||
What moves to per-connection state:
|
||||
- Port forwarding proxy logic
|
||||
|
||||
### RawFramingInterface
|
||||
**Current implementation note**: `SshSession::recv()` and `SshSession::send()`
|
||||
are stubs. The bridge from SSH channels to `InterfaceEvent` frames is
|
||||
scheduled for Phase 2 implementation (see integration-plan.md Phase 2.1).
|
||||
|
||||
### RawFramingInterface (StreamInterface)
|
||||
|
||||
Reads 4-byte big-endian length prefix + JSON `EventEnvelope` frames directly
|
||||
from the transport stream. No SSH wrapping. No channel multiplexing — the
|
||||
@@ -88,134 +167,210 @@ entire stream is a single call protocol channel.
|
||||
```rust
|
||||
pub struct RawFramingInterface;
|
||||
|
||||
impl Interface for RawFramingInterface {
|
||||
impl StreamInterface for RawFramingInterface {
|
||||
type Session = RawFramingSession;
|
||||
// Reads length-prefixed EventEnvelope frames from the stream
|
||||
}
|
||||
```
|
||||
|
||||
Used for:
|
||||
- DNS control channel (DNS transport + raw framing)
|
||||
- Local service mesh (TCP + raw framing, no SSH overhead)
|
||||
- Browser direct call protocol (WebTransport + raw framing, future)
|
||||
- Secure mesh (TLS + raw framing)
|
||||
- WebTransport direct call protocol (future: WebTransport + raw framing)
|
||||
|
||||
### DNS Control Channel
|
||||
Auth for raw framing: `AuthToken` in frame header, resolved via
|
||||
`IdentityProvider::resolve_from_token()`.
|
||||
|
||||
A (DNS transport, raw framing interface) pair. The DNS transport encodes
|
||||
`EventEnvelope` frames as DNS query/response pairs. The raw framing interface
|
||||
parses them directly — **NOT** SSH inside DNS.
|
||||
**Current implementation note**: `RawFramingInterface::accept()` returns an
|
||||
error. Frame reading/writing is scheduled for Phase 2 implementation (see
|
||||
integration-plan.md Phase 2.2).
|
||||
|
||||
### HttpInterface (MessageInterface)
|
||||
|
||||
Accepts standard HTTP requests and maps them to call protocol operations:
|
||||
|
||||
```
|
||||
Client: Encode EventEnvelope as base32 DNS query labels
|
||||
→ DNS Transport → DNS Server → Raw Framing Interface → Call Protocol Handler
|
||||
|
||||
Server: Return EventEnvelope as DNS TXT record response
|
||||
← Raw Framing Interface ← DNS Transport ← Call Protocol Handler
|
||||
POST /v1/{namespace}/{op} → registry.invoke(namespace, op, input) (mutation)
|
||||
GET /v1/{namespace}/{op} → registry.invoke(namespace, op, input) (query)
|
||||
GET /v1/{namespace}/{op} SSE → registry.subscribe(namespace, op, input) (subscription)
|
||||
GET /v1/schema → registry.list_operations()
|
||||
```
|
||||
|
||||
### Valid (Transport, Interface) Pairs
|
||||
Auth: `Authorization: Bearer <token>` header, resolved via
|
||||
`IdentityProvider::resolve_from_token()`. Both AuthTokens and API keys are
|
||||
accepted.
|
||||
|
||||
| Transport | Interface | Use case |
|
||||
|-----------|-----------|----------|
|
||||
| TLS | SSH | Standard alknet tunnel |
|
||||
| TCP | SSH | Plain SSH tunnel |
|
||||
| iroh | SSH | P2P SSH tunnel |
|
||||
| DNS | raw framing | DNS control channel |
|
||||
| WebTransport | SSH | Browser SSH tunnel (future) |
|
||||
| WebTransport | raw framing | Browser call protocol (future) |
|
||||
| TCP | raw framing | Direct call protocol, local mesh |
|
||||
The HTTP interface runs inside the existing stealth mode byte-peek architecture:
|
||||
after a TLS handshake, the server peeks at the first bytes. If they're
|
||||
`SSH-2.0-`, the stream goes to `SshInterface`. Otherwise, the stream goes to
|
||||
the axum HTTP router.
|
||||
|
||||
### InterfaceConfig
|
||||
**Phase 2 scope**: Auth middleware, stealth handoff, and default 404 handler
|
||||
only. Specific operation routes and path conventions are Phase 5+. The
|
||||
`ListenerConfig::Http` variant spawns an axum router that reaches auth context;
|
||||
routing inside axum is a later concern.
|
||||
|
||||
Different interfaces require different configuration:
|
||||
### DnsInterface (MessageInterface)
|
||||
|
||||
A DNS server that encodes/decodes `EventEnvelope` frames as DNS query/response
|
||||
pairs. AuthToken is embedded in DNS query labels. Resolution via
|
||||
`IdentityProvider::resolve_from_token()`.
|
||||
|
||||
This is a `MessageInterface` — it manages its own transport (UDP/TCP port 53)
|
||||
and handles individual DNS queries as request/response pairs. DNS is NOT a
|
||||
transport.
|
||||
|
||||
**Phase**: DNS interface implementation is Phase 5+. The `ListenerConfig::Dns`
|
||||
variant and `DnsInterface` stub are defined now; implementation is deferred.
|
||||
|
||||
### Stream-Based Interface Pairs
|
||||
|
||||
| Transport | StreamInterface | Credential Presentation | Use case |
|
||||
|-----------|---------------|------------------------|----------|
|
||||
| TLS | SshInterface | SSH key handshake | Standard alknet tunnel |
|
||||
| TCP | SshInterface | SSH key handshake | Plain SSH tunnel |
|
||||
| iroh | SshInterface | SSH key handshake | P2P SSH tunnel |
|
||||
| TCP | RawFramingInterface | AuthToken in frame header | Local service mesh |
|
||||
| TLS | RawFramingInterface | AuthToken in frame header | Secure mesh |
|
||||
| WebTransport | RawFramingInterface | AuthToken in CONNECT request | Browser call protocol (future) |
|
||||
|
||||
### Message-Based Interface Pairs
|
||||
|
||||
| MessageInterface | Credential Presentation | Owns transport? | Use case |
|
||||
|-----------------|------------------------|----------------|----------|
|
||||
| HttpInterface | `Authorization: Bearer` header | Yes (axum) | REST API, dashboard, integrations |
|
||||
| DnsInterface | AuthToken in query labels | Yes (DNS server) | Censorship-resistant control channel |
|
||||
| WebSocketInterface | AuthToken in handshake | Yes (WS server) | Browser persistent connection (future) |
|
||||
|
||||
Message-based interfaces manage their own transport. They don't need a
|
||||
`Transport` from Layer 1 — they ARE the transport+interface combined.
|
||||
|
||||
### ListenerConfig
|
||||
|
||||
The server's accept loop configuration covers both stream and message interfaces:
|
||||
|
||||
```rust
|
||||
pub enum InterfaceConfig {
|
||||
Ssh(SshInterfaceConfig),
|
||||
RawFraming(RawFramingConfig),
|
||||
pub enum ListenerConfig {
|
||||
Stream {
|
||||
transport: TransportKind,
|
||||
interface: StreamInterfaceKind,
|
||||
},
|
||||
Http {
|
||||
bind_addr: SocketAddr,
|
||||
tls: bool,
|
||||
stealth: bool, // byte-peek protocol detection on shared port
|
||||
},
|
||||
Dns {
|
||||
bind_addr: SocketAddr,
|
||||
tls: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct SshInterfaceConfig {
|
||||
pub auth: Arc<dyn IdentityProvider>,
|
||||
pub forwarding: Arc<ArcSwap<DynamicConfig>>, // for ForwardingPolicy
|
||||
pub host_key: Arc<PrivateKey>,
|
||||
pub enum StreamInterfaceKind {
|
||||
Ssh,
|
||||
RawFraming,
|
||||
}
|
||||
|
||||
pub struct RawFramingConfig {
|
||||
// No SSH-specific config needed
|
||||
// Auth is handled by the transport layer (e.g., token auth for WebTransport)
|
||||
// or by the call protocol layer
|
||||
pub enum TransportKind {
|
||||
Tcp,
|
||||
Tls { server_name: Option<String> },
|
||||
Iroh { endpoint_id: String },
|
||||
WebTransport, // Phase 5+: tag only, no acceptor yet
|
||||
}
|
||||
```
|
||||
|
||||
### Auth Across Interfaces
|
||||
Note: `TransportKind::Dns` does NOT exist. DNS is a `MessageInterface`, not a
|
||||
transport. The `ListenerConfig::Dns` variant handles DNS listener configuration
|
||||
directly.
|
||||
|
||||
- **SshInterface**: Auth happens during SSH handshake via
|
||||
`IdentityProvider::resolve_from_fingerprint()`. The authenticated `Identity`
|
||||
is attached to the session.
|
||||
- **RawFramingInterface**: Auth is handled by the transport (e.g., token auth
|
||||
for WebTransport via `IdentityProvider::resolve_from_token()`) or by the call
|
||||
protocol layer (operation-level ACL).
|
||||
### Credential Presentation Across Interfaces
|
||||
|
||||
Both paths produce the same `Identity` type (ADR-029).
|
||||
Every interface resolves to the same `Identity` through `IdentityProvider`:
|
||||
|
||||
```
|
||||
SSH fingerprint → IdentityProvider::resolve_from_fingerprint → Identity
|
||||
AuthToken (Bearer) → IdentityProvider::resolve_from_token → Identity
|
||||
API key (Bearer) → IdentityProvider::resolve_from_token → Identity
|
||||
DNS embedded token → IdentityProvider::resolve_from_token → Identity
|
||||
```
|
||||
|
||||
The credential presentation differs per (Transport, Interface) pair, but the
|
||||
resolution result is always an `Identity`. See [definitions.md](definitions.md)
|
||||
for the full table and terminology rules.
|
||||
|
||||
### Server Accept Loop
|
||||
|
||||
With the Interface trait, the accept loop becomes:
|
||||
With both stream and message interfaces, the accept loop becomes:
|
||||
|
||||
```rust
|
||||
for listener in listeners {
|
||||
let (transport, interface) = listener;
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let stream = transport.accept().await?;
|
||||
let session = interface.accept(stream, &config).await?;
|
||||
// session produces call protocol events
|
||||
// call protocol handler is interface-agnostic
|
||||
match listener {
|
||||
ListenerConfig::Stream { transport, interface } => {
|
||||
// Spawn accept loop: transport.accept() → interface.accept(stream)
|
||||
}
|
||||
});
|
||||
ListenerConfig::Http { bind_addr, tls, stealth } => {
|
||||
// Spawn axum HTTP server on bind_addr
|
||||
// If stealth: byte-peek after TLS, route SSH vs HTTP
|
||||
}
|
||||
ListenerConfig::Dns { bind_addr, tls } => {
|
||||
// Spawn DNS server on bind_addr
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constraints
|
||||
|
||||
- The Interface trait must accommodate both SSH's channel multiplexing and raw
|
||||
framing's single-stream model through the same abstraction.
|
||||
- `SshInterface` is the most invasive refactoring in Phase 1. The existing
|
||||
`ServerHandler` owns auth, channel management, and proxy logic — extracting
|
||||
these cleanly requires careful design (integration-plan, Phase 1.8).
|
||||
- DNS transport implementation is Phase 4 work. The `TransportKind::Dns` variant
|
||||
and `RawFramingInterface` are defined now; implementation is deferred.
|
||||
- WebTransport is Phase 4 work. The `TransportKind::WebTransport` variant is a
|
||||
tag only for now.
|
||||
- `StreamInterface` and `MessageInterface` are independent traits with different
|
||||
signatures, lifecycles, and transport ownership. No common super-trait (ADR-035).
|
||||
- `SshInterface` is the most invasive refactoring. The existing `SshHandler`
|
||||
owns auth, channel management, and proxy logic — extracting these cleanly
|
||||
requires careful design (integration-plan Phase 1.8, completed in Phase 1).
|
||||
- DNS interface implementation is Phase 5 work. `DnsInterface` is defined as a
|
||||
`MessageInterface` stub; implementation is deferred.
|
||||
- HTTP interface Phase 2 scope is limited to auth middleware and stealth handoff.
|
||||
Specific operation routes are Phase 5+.
|
||||
- WebTransport is Phase 5 work. `TransportKind::WebTransport` and
|
||||
`StreamInterfaceKind::WebTransport` are tags only for now.
|
||||
- `TransportKind::Dns` does not exist. DNS is a `MessageInterface`, not a
|
||||
transport. This was `TransportKind` enum pollution from an earlier design.
|
||||
- The `Interface` trait (singular) in the current codebase needs to be renamed
|
||||
to `StreamInterface`. This is a rename, not a semantic change.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **OQ-IF-01**: How does the `Interface` session type relate to the call
|
||||
protocol's `EventEnvelope` stream? Does every session implement
|
||||
`Stream<Item=EventEnvelope>`? This needs design during Phase 1.8.
|
||||
- **OQ-IF-02**: ~~Should `SshInterface` own the `ForwardingPolicy` check for
|
||||
`channel_open_direct_tcpip`, or should that move to Layer 3?~~ **Resolved**:
|
||||
ForwardingPolicy is Layer 3, but channel open/close lifecycle is Layer 2.
|
||||
SshInterface reports channel requests to Layer 3; Layer 3 applies policy.
|
||||
|
||||
- **OQ-IF-02**: Should `SshInterface` own the `ForwardingPolicy` check for
|
||||
`channel_open_direct_tcpip`, or should that move to Layer 3? Current thinking:
|
||||
the forwarding check is a Layer 3 concern (it's policy, not session mechanics),
|
||||
but the channel open/close lifecycle is Layer 2. The Interface reports channel
|
||||
open requests to Layer 3; Layer 3 applies `ForwardingPolicy` and tells
|
||||
Layer 2 whether to proxy.
|
||||
- **OQ-P2-01**: Should `MessageInterface` and `StreamInterface` share a common
|
||||
trait? **Recommendation**: No. Independent traits with different signatures,
|
||||
lifecycles, and transport ownership. A common super-trait adds complexity
|
||||
without clear benefit. (See ADR-035.)
|
||||
|
||||
- **OQ-P2-02**: Should the HTTP interface share a port with the SSH listener?
|
||||
**Recommendation**: Start with separate ports. ALPN multiplexing on port 443
|
||||
is a future optimization that doesn't change the interface abstraction.
|
||||
Stealth mode byte-peek already handles shared-port detection for the common
|
||||
case.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
| ADR | Decision | Summary |
|
||||
|-----|----------|---------|
|
||||
| [026](decisions/026-transport-interface-separation.md) | Three-layer model | SSH is Layer 2, not Layer 1 |
|
||||
| [035](decisions/035-streaminterface-messageinterface-split.md) | StreamInterface / MessageInterface | Two trait categories at Layer 2 |
|
||||
| [033](decisions/033-operationenv-irpc-call-protocol.md) | OperationEnv | Protocol is interface-agnostic |
|
||||
| [029](decisions/029-identity-core-type.md) | Identity as core type | Auth resolution across interfaces |
|
||||
| [031](decisions/031-forwarding-policy.md) | Forwarding policy | Layer 3 policy applied to Layer 2 channel requests |
|
||||
|
||||
## References
|
||||
|
||||
- [research/integration-plan.md](../research/integration-plan.md) — Phase 1.8, valid (Transport, Interface) pairs
|
||||
- [research/core.md](../research/core.md) — DNS transport, three-layer model
|
||||
- [ADR-026](decisions/026-transport-interface-separation.md) — Transport/interface separation
|
||||
- [definitions.md](definitions.md) — Terminology disambiguation, credential presentation
|
||||
- [research/phase2/interface-model.md](../research/phase2/interface-model.md) — Full StreamInterface/MessageInterface analysis
|
||||
- [research/phase2/tls-transport.md](../research/phase2/tls-transport.md) — HTTP interface, stealth handoff, ListenerConfig
|
||||
- [research/integration-plan.md](../research/integration-plan.md) — Phase 1.8, Phase 2.1-2.7
|
||||
- [transport.md](transport.md) — Transport trait (unchanged at Layer 1)
|
||||
- [server.md](server.md) — Current ServerHandler (will become SshInterface)
|
||||
- [auth.md](auth.md) — Credential presentation per (Transport, Interface) pair
|
||||
- [identity.md](identity.md) — IdentityProvider, auth across interfaces
|
||||
Reference in New Issue
Block a user