Port the call + channels architecture documentation from the alknet mono-repo into docs/architecture/, renumbered as alkcall ADR-001..045. Renumbering map (alknet -> alkcall): Core: 001,002,004,006,007,011,065,070,092,014,050,091 -> 001-012 Call: 005,064,012,023,015,022,024,016,049,017,028,029,030,032,066,069,067,068 -> 013-030 Shared: 003,009,013 -> 031-033 Channels: 071,093,072,073,074,075,076,094,079,080,081,089 -> 034-045 3 superseded/reversed ADRs kept for historical trail: - ADR-013 (irpc foundation, superseded by ADR-014) - ADR-023 (peer-scoped filtering, superseded by ADR-024) - ADR-077 (TTY inside channels, reversed by ADR-035 — not ported, TTY-only) Ported docs (11 spec files + README + open-questions): - call-README.md, call-protocol.md, operation-registry.md, client-and-adapters.md - channels-README.md, channels-overview.md, channels-wire.md, channels-connection.md, channels-adapter.md, channel-operations.md, channel-client.md - README.md (index with doc table, ADR table grouped by category, key principles) - open-questions.md (lean — 30 OQs, renumbered OQ-01..030; includes new OQ-22 for the pub/sub gap) Cross-reference rewriting: - All ADR-NNN references rewritten single-pass (no chaining bug) - Markdown link paths fixed - Title lines aligned with filenames - Non-ported ADR refs (052, 082, 086, etc.) left as-is with README note The open-questions.md includes OQ-22 (new): the call protocol pub/sub gap — subscribe exists but pub does not, needed for channels channel/resources/subscribe fan-out. This is the next ADR to write (alkcall ADR-046).
78 lines
5.5 KiB
Markdown
78 lines
5.5 KiB
Markdown
# ADR-013: irpc as Call Protocol Foundation
|
|
|
|
## Status
|
|
|
|
~~Accepted~~ → **Superseded** by [ADR-014](014-irpc-never-integrated-hand-rolled-framing.md)
|
|
|
|
> **Superseded 2026-07-09.** This ADR accepted "irpc as the call protocol
|
|
> foundation" based on the previous architecture's use of irpc. When the
|
|
> call protocol was implemented, it turned out that **no `.rs` file in the
|
|
> workspace ever imported irpc** — the `irpc` / `irpc-derive` workspace deps
|
|
> were a Cargo.toml entry with no corresponding import. The wire protocol
|
|
> (`crates/alknet-call/src/protocol/wire.rs`) is hand-rolled length-prefixed
|
|
> JSON; the `EventEnvelope` shape was derived from the `@alkdev/pubsub`
|
|
> TypeScript prior art (ADR-033), not from irpc. ADR-014 supersedes this
|
|
> ADR and records the actual state: hand-rolled framing, no irpc
|
|
> integration. The architectural properties this ADR sought (proven
|
|
> length-prefixed JSON framing, cross-language JSON wire format, streaming)
|
|
> are preserved by the hand-rolled implementation. The text below is kept
|
|
> as the historical record of the decision that was made (and never
|
|
> implemented as stated).
|
|
|
|
## Context
|
|
|
|
The call protocol (alknet-call) provides structured RPC — operations, request/response, streaming subscriptions, and pub/sub. This is the primary interface for programmatic interaction with an alknet node. It needs to work across platforms: Rust clients, TypeScript/JavaScript clients (via NAPI), WASM targets, and any language that can speak the wire format.
|
|
|
|
The previous implementation used `irpc` for the call protocol's operation registry, framing, and service patterns. irpc provides:
|
|
- An operation registry with schema-based discovery
|
|
- Length-prefixed JSON framing (EventEnvelope)
|
|
- Request/response and streaming patterns
|
|
- Type-safe operation definitions via derive macros
|
|
|
|
The call protocol is derived from a TypeScript implementation (`@alkdev/operations`, `@alkdev/pubsub`) that informed the design of the operation registry, EventEnvelope framing, and adapter patterns (from_openapi, from_mcp, from_call). This bidirectional composition capability is strategically important. The TypeScript code is a reference that informed the Rust design — it is not a parallel implementation (see ADR-033).
|
|
|
|
## Decision
|
|
|
|
alknet-call uses irpc as its foundation. The `CallAdapter` implements `ProtocolHandler` on ALPN `alknet/call` and delegates to irpc's operation registry, framing, and dispatch.
|
|
|
|
irpc is not replaced or wrapped in an abstraction layer — it IS the call protocol's core. The relationship is:
|
|
- irpc provides: operation registry, schema discovery, frame encoding/decoding, request/response routing, streaming
|
|
- alknet-call provides: the ProtocolHandler adapter (BiStream → irpc), AuthContext integration, access control checks, the ALPN registration
|
|
|
|
This means:
|
|
- The wire format is irpc's EventEnvelope framing — length-prefixed JSON
|
|
- Operation schemas follow irpc's schema model — JSON Schema compatible
|
|
- The TypeScript operation and pub/sub patterns that can import OpenAPI schemas, wrap MCP servers, and expose operations as endpoints are supported at the protocol level — the adapter contract (from_*, to_*) is defined in Rust (see ADR-033)
|
|
- Future NAPI and WASM clients speak the same wire format — alknet-napi projects the Rust call protocol client to Node.js; a browser SDK can be adapted from the existing TypeScript code
|
|
|
|
The `VaultProtocol` in alknet-vault previously used irpc as its service
|
|
protocol. ADR-025 dropped irpc from the vault — the vault uses direct method
|
|
calls on `VaultServiceHandle`, not irpc dispatch. irpc remains the
|
|
foundation for alknet-*call* (the call protocol), not for alknet-*vault*.
|
|
See ADR-025 for the rationale (security default inversion: the vault is
|
|
local-only by construction, not remote-capable by default).
|
|
|
|
## Consequences
|
|
|
|
**Positive:**
|
|
- Proven operation registry and framing — irpc is already tested in production (iroh uses it)
|
|
- JSON Schema compatible — OpenAPI import, MCP tool exposure, cross-language client generation
|
|
- No need to design a custom RPC wire format — irpc's is already battle-tested
|
|
- The call protocol inherits irpc's streaming and subscription patterns
|
|
|
|
**Negative:**
|
|
- alknet-call depends on irpc — if irpc has limitations or bugs, we're affected (mitigated: irpc is lightweight and we can fork if needed)
|
|
- JSON framing is not the most compact binary format — for high-throughput scenarios, a binary codec could be added later as an irpc extension
|
|
- irpc's derive macros add a compilation dependency — but this is standard for Rust RPC frameworks
|
|
- The call protocol's cross-language story depends on irpc's wire format being documented and stable (mitigated: it's length-prefixed JSON, which is inherently cross-language)
|
|
|
|
## References
|
|
|
|
- **Superseding ADR**: [ADR-014](014-irpc-never-integrated-hand-rolled-framing.md) — irpc was never integrated; hand-rolled framing is the actual state
|
|
- ADR-033: Rust as canonical implementation (the `@alkdev/pubsub` prior art the `EventEnvelope` shape was actually derived from)
|
|
- ADR-025: Vault local-only dispatch (dropped irpc from the vault; ADR-014 confirms irpc was never in alknet-call either)
|
|
- Pivot proposal: `docs/research/pivot/alpn-service-architecture.md`
|
|
- ADR-031: Crate decomposition
|
|
- ADR-003: Auth as shared core (IdentityProvider)
|
|
- Call protocol wire format (actual): `crates/alknet-call/src/protocol/wire.rs`
|
|
- The previous architecture had an equivalent decision in ADR-019 (bidirectional call protocol with EventEnvelope framing), which is archived in the reference implementation at `/workspace/@alkdev/alknet-main/`. |