Files
alkcall/docs/architecture/open-questions.md
T
glm-5.3-flash 5f019c02f1 docs: re-key architecture docs off the alknet mono-repo — speak as alkcall
The ported docs still framed alknet siblings (alknet-core/-call/-channels/
-http/-tls/-client) as live load-bearing context of this crate. With the
extraction complete and alknet headed for glue-level replacement, the docs
now speak as alkcall and cite the real sibling crates (alkhttp, alktls,
alktty, alktunnels, alktrader).

- ADR-054 (new): re-own the verifier-selection rule in-crate — three
  remote roles (public X.509 endpoint / transport relay / hub),
  ServerCertVerifier by PeerEntry presence, RemoteIdentity None
  load-bearing. Provenance: alknet ADR-034 (never ported), demoted to
  footnote. Resolves OQ-21's cross-repo authority gap.
- OQ-43 (new, deferred(scope)): composition-layer ownership and shape
  after the alknet replacement — the named receiver for the
  downstream-side deferrals (OQ-33 endpoint layer, OQ-41 QUIC-native
  substrate, OQ-42 hub broker, OQ-19 persistence adapters, ADR-045 dial
  seam). README layering diagram redrawn with the composition layer as
  TBD-ownership instead of "alknet / alknode" in concrete.
- Spec docs reframed: titles, self-references, dial/TLS/from_jsonschema
  homes, malformed legacy cite rows removed; dead relative research
  paths qualified as source-repo paths.
- ADR amendments (append-only): 027 (alknet-http -> alkhttp), 031 (post-
  extraction state), 044 (split landed as modules in alkcall), 045
  (name normalization; citation-space note).
- Citation hygiene: unprefixed/colliding source-repo cites prefixed
  ("alknet ADR-NNN (provenance)") across 009/011/022/034/035/038/040/
  043/044/045; stale OQ cites corrected (session registries OQ-04, batch
  semantics OQ-03, verifier rule ADR-054); stale anchors fixed.
- README provenance note consolidated; citation conventions documented.

Verification: markdown link check (all local links resolve, all in-crate
ADR cites point at existing decisions/ files); table integrity check
(uniform row widths across open-questions.md and edited tables); residual
alknet-mention sweep (only provenance/historical/intentional framing
remains); architecture review subagent pass with zero remaining criticals.
Docs-only change — no code paths touched.
2026-10-13 10:53:47 +00:00

127 lines
14 KiB
Markdown

---
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. |