--- status: current last_updated: 2026-10-13 (composition-layer re-key) --- # Open Questions Open questions are tracked here, organized by theme. Each question has a status, priority, and (when resolved) a resolution citing the ADR. **Status values**: `open`, `resolved`, `deferred(scope)`, `deferred(unclear)`, `partially resolved`, `dissolved`. ## Call Protocol | OQ | Title | Status | Priority | Resolution | |----|-------|--------|----------|------------| | OQ-01 | Call protocol scope within a connection | resolved | medium | ADR-015 — stream model, multiplexing | | OQ-02 | Operation path format and routing scope | resolved | medium | `/{service}/{op}` is the correct design | | OQ-03 | Batch operation semantics | resolved | low | Correlated call.requested events | | OQ-04 | Session-scoped operation registries | resolved | medium | ADR-019 — OperationEnv trait layering | | OQ-05 | Abort cascade semantics for nested calls | resolved | high | ADR-020 | | OQ-06 | Privilege model and authority context | resolved | high | ADR-017 | | OQ-07 | Handler identity registration path and composition authority | resolved | high | ADR-018 | | OQ-08 | Operation error schemas | resolved | high | ADR-016 | | OQ-09 | Safe vault operations for call protocol exposure | resolved | high | ADR-010 — none exposed | | OQ-10 | ~~Remote-safe marking shape~~ | dissolved | medium | ADR-024 — remote_safe/trusted_peer retired | | OQ-11 | OperationAdapter error type (AdapterError variants) | resolved | medium | DiscoveryFailed, SchemaParse, Transport, Unauthorized, SamePeerCollision | | OQ-12 | from_call re-import trigger | resolved | low | ADR-028 — manual free function | | OQ-13 | from_call namespace collision | resolved | low | Same-peer = error; cross-peer dissolved (ADR-024) | | OQ-14 | CallClient TLS client-auth | resolved | high | quinn client-auth; key-type-aware verification | | OQ-15 | PeerRef::Any routing policy | resolved | low | Insertion-order first-match | | OQ-16 | services/list-peers re-export semantics | resolved | low | Opt-in; services/list is own-ops-only | | OQ-17 | Multi-hop federation | deferred(scope) | low | One-hop model is the commitment; multi-hop is a feature extension | | OQ-18 | PeerId — crypto identity vs stable logical id | resolved | high | ADR-025 — PeerId = Identity.id (stable) | | OQ-19 | Persistent peer registry | resolved | medium | ADR-025 — core trait + in-memory default; persistence adapters separate | | OQ-20 | ~~API key asymmetry~~ | dissolved | medium | PeerEntry supports multiple credential paths | | OQ-21 | X.509 outgoing-only case | resolved | medium | Three remote roles; PeerEntry asymmetry correct. Re-owned in-crate by ADR-054 — the verifier-selection rule that binds alkcall's surface (`RemoteIdentity: None` semantics, `ServerCertVerifier` by `PeerEntry` presence) is decided here, not by the un-ported source ADR (which is demoted to provenance). | ## Call Protocol — Pub/Sub | OQ | Title | Status | Priority | Resolution | |----|-------|--------|----------|------------| | OQ-22 | Call protocol pub/sub primitive — pub to go with sub | resolved | high | The question had two halves, both now decided. (1) The primitive: ADR-046 delivers `OperationType::Pub` + `HandlerKind::Sink` + `call.published` wire event + `invoke_sink()` dispatch + `CallConnection::publish()`. (2) Placement of the fan-out/broker: **out of scope for alkcall** — the call protocol is point-to-point; a topic registry that outlives individual calls is a routing concern that sits above it. The hub composes the broker on top of the `Pub`/`Sub` primitives (ADR-047 §"What this ADR decides" Gap B). The broker design itself is tracked as OQ-42. See §"Pub/Sub Gap" below. | | OQ-42 | Hub message broker (topic registry, `Pub`↔`Sub` matching, N-consumer fan-out) | deferred(scope) | medium | Deferred downstream: the broker is a hub composition layering on alkcall's `Pub`/`Sub` primitives, not an alkcall deliverable (OQ-22 placement resolution, ADR-047 Gap B). **Blocked on**: a concrete producing crate committing to fan-out — a spec that requires one produced topic with N independent spoke subscribers (plausible first candidate: alktrader market-data distribution; ADR-046 §fan-out records the consumer-blind rationale). Not circular — the primitives it composes on are delivered and load-bearing (ADR-046), and no alkcall API surface advertises or depends on the broker. The hub itself lives in the composition layer (owner — OQ-43). | ### Pub/Sub Gap The call protocol's `StreamingHandler` / `invoke_streaming()` path (ADR-021) is point-to-point: a `call.requested` arrives, the handler produces a stream of `call.responded` events back to that one caller. There was no mechanism for a producer to stream data *to* a responder (producer→consumer streaming), and no fan-out (one producer, N consumers). **ADR-046** resolves the directional gap: `OperationType::Pub` is the producer→consumer streaming complement to `Sub` (was `Subscription`, renamed for symmetry). `HandlerKind::Sink` is the consuming handler type. `call.published` is the wire event carrying stream chunks. `invoke_sink()` is the dispatch path. `CallConnection::publish()` is the client method. The `Subscription` variant is renamed to `Sub` (wire string `"sub"`). **Placement decided: the broker is not an alkcall deliverable.** The fan-out/broker mechanism (topic registry, `Pub`↔`Sub` matching, N-consumer fan-out) sits above the call protocol: the call protocol is point-to-point (one initiator, one responder, a stream between them); a topic registry that outlives individual calls is a different lifecycle and a different concern. The broker is a hub composition on top of the `Pub`/`Sub` primitives — ADR-047 §"What this ADR decides" (Gap B) places it out of scope for alkcall. The `Pub` primitive is the load-bearing piece the broker composes on (delivered, ADR-046); ADR-053 served the broker's originally-named first consumer (`channel/resources/subscribe`) with enumerators instead. **The broker design is downstream work (OQ-42, deferred(scope)).** It unblocks when a concrete producing crate commits to fan-out. The original deferral's rationale (ADR-046 §fan-out — designing the broker without its first real consumer produces a half-design) still holds: the honest state is a tracked, concretely-blocked deferral, not a pending promise. The "channels session" phrasing in ADR-046 §fan-out should be read as this placement decision (the channels work has since landed; what remains unbuilt is the downstream hub broker). ## Channels | OQ | Title | Status | Priority | Resolution | |----|-------|--------|----------|------------| | OQ-23 | Full channel-level flow-control windowing | deferred(scope) | low | Bounded-buffer decided (ADR-040); full windowing blocked on HOL-blocking deployment observation | | OQ-24 | Channels add/strip API shape | resolved | low | Resolved by the shipped surface: the strip is **integrated into the read/write path** — the demux loop consumes `read_header`/`write_chunk` so a handler's `BiStream` yields payload chunks and never sees the 8-byte header (ADR-035; matches ADR-037's payload-parse contract). The low-level primitives (`parse_header`/`write_header`/`read_header`/`write_chunk`/`write_eof` in `channels::wire`) remain exposed as the standalone utility for tooling/codegen, but no handler-facing add/strip wrapper exists or is needed. | | OQ-31 | `channel/open` ACL granularity | resolved | high | ADR-047 — `channel/open` dissolves into per-ALPN ops; each op has its own `access_control` | | OQ-32 | Quota lifecycle (opener vs closer, transport drop) | resolved | high | ADR-047 §7 — the per-connection opener ledger; decrement on every teardown path, keyed by opener | | OQ-33 | Per-identity connection cap (endpoint layer) | deferred(scope) | low | Named as a separate layer (ADR-047 §"ALPN category reframe" references the findings); belongs at the endpoint/connect-accept layer of the composition stack, not channels. Named to stop the re-tangle. The endpoint layer's home is the composition layer (owner — OQ-43; today it lives in the alknet glue). | | OQ-34 | `channel_open` marker wire format | resolved | medium | ADR-047 §2 — boolean `"channel_open": true` in `services/schema`; ALPN derivable from op name | | OQ-35 | `OperationEnv::channel_manager()` coupling | resolved | high | ADR-047 §4 — extension trait `ChannelOperationEnv` in `channels-call`; call crate stays free of channels types | | OQ-36 | `channel_id` allocation in Pub case | resolved | medium | ADR-047 §5 — "connection owner allocates" (the side that holds the `ChannelManager`); amends "responder allocates" | | OQ-37 | `from_call` relay wrapper for marked ops | resolved | medium | ADR-022 amendment 2026-10-13 — there is no `from_call` relay wrapper, and there won't be one: marked (`channel_open`) announcements are refused with `MARKED_SPEC_UNSUPPORTED`, and `register_imported`/`register_imported_all` refuse marked specs (ADR-047 amendment 4 §4.8) — a marked op's data plane (the allocated channel's `BiStream`) has no overlay path. The sanctioned compositions are named in the refusal: `ChannelRelay::register_relay_openable` (ADR-051) for a relaying hub, `ChannelClient` (ADR-043) for direct consumption. `from_call`'s discovery still reconstructs the marker (ADR-047 Gap F) for reporting, but importing a marked spec is refused at the overlay landing. | | OQ-38 | ALPN→path-segment mapping | resolved | low | ADR-047 §"Negative" — strip the `alk/` prefix; ALPNs without that prefix use the full ALPN string (rare, two-way-door) | | OQ-39 | `channel/control` control-handle surface | resolved | medium | ADR-052 — the control-handle surface is implemented: per-channel control callbacks contributed by the ALPN establisher (`Establishment::with_control`), wrapper-installed and teardown-removed in the manager; the op routes the ALPN-specific `message` and answers `channel:no_control_handle` when the channel's ALPN registered none. The stub (`channel:control_not_implemented`) was removed with the single-publish fix (reviews 010 S-02 / 011). | | OQ-40 | `channel/resources/subscribe` live subscription | resolved | medium | ADR-053 — the live subscription is implemented: one `ResourceEnumerator` per openable ALPN (provided by the producing crate, snapshot-then-changes), aggregated by the handler over the scan-once snapshot; an empty registry emits one `{ "resources": [] }` seed and stays open. The one-shot error stub was removed with the single-publish fix (reviews 010 S-03 / 011). The alktunnels discovery-UI note (review 006 E-02) is served by the enumerator surface. | | OQ-41 | QUIC-native multi-stream substrate | deferred(scope) | medium | **Deferred downstream** — the substrate lives in the dial/accept layer, which alkcall does not own. Only the in-line substrate mode is implemented (single bidi stream, header-demuxed N channels); the QUIC-native multi-stream mode (accept remaining bidi streams, read headers off each — ADR-034 §substrate modes) belongs in the composition layer's QUIC accept loop. **Blocked on**: the composition layer's multi-stream substrate integration (owner — OQ-43; today the alknet glue, being replaced). **Impacts**: none in-crate — the wire format and demux loop are correct for both substrates; alkcall stays transport-agnostic (no QUIC dependency, WASM-compatible). | ## Core Types | OQ | Title | Status | Priority | Resolution | |----|-------|--------|----------|------------| | OQ-25 | BiStream type definition | resolved | high | ADR-005 — trait, Connection parameter | | OQ-26 | AuthContext resolution timing | resolved | high | ADR-003 — hybrid resolution | | OQ-27 | ALPN string naming convention | resolved | medium | ADR-004 — alk/ prefix | | OQ-28 | Dynamic handler registration | resolved | low | ADR-019 — curated static, overlays dynamic | | OQ-29 | Handler-level auth resolution observability | resolved | medium | set_identity() on Connection for observability | | OQ-30 | Dynamic resource ownership | resolved | high | ADR-011 — OwnershipProvider, resource_id_path | ## Composition Layer The layer above alkcall that owns dial/accept, hub composition, and endpoint concerns. As of 2026-10 that layer is the alknet glue (the alknet mono-repo's crates: its client dial helper `AlknetClient`, its endpoint crate, its hub wiring); the mono-repo itself is being replaced by thin glue over alkcall and its sibling protocol/host crates (alkhttp, alktty, alktunnels, alktrader, alktls, …). Several deferred questions name "downstream" as their receiver — this OQ is the named receiver for all of them, so the deferrals survive the replacement rather than being repo-pinned to a codebase slated for deletion. | OQ | Title | Status | Priority | Resolution | |----|-------|--------|----------|------------| | OQ-43 | Composition-layer ownership and shape after the alknet replacement | deferred(scope) | medium | **Deferred scope**: who owns dial/accept/hub composition, the endpoint layer (OQ-33), the QUIC-native substrate (OQ-41), and persistence adapters (OQ-19) after the alknet mono-repo is replaced — and whether that composition is one thin glue workspace (an `alknode`-type crate consuming alkcall + the sibling crates) or several small glue binaries. **Blocked on**: the glue-workspace decision being committed (the stated direction is "a couple kloc of glue", but the shape — one workspace vs several binaries — and its ownership are not yet committed). **Impacts**: re-keys the deferrals OQ-33, OQ-41, OQ-42, OQ-19; determines the dial seam's future home (ADR-045's `AlknetClient`, today in the alknet glue); determines where hub-relay wiring templates are consumed (ADR-042, ADR-051). **Not blocking** any alkcall v0.9 API surface — alkcall is transport-blind and composition-blind by construction (ADR-031, ADR-035). **Cross-references**: OQ-33, OQ-41, OQ-42, OQ-19; ADR-045, ADR-031, ADR-042, ADR-051. |