Files
alknet/docs/architecture/decisions/005-irpc-as-call-protocol-foundation.md
glm-5.2 7dda6eec68 docs(architecture): add ADR-025 — vault local-only dispatch, drop irpc
Drops irpc from alknet-vault entirely. The vault's dispatch is now direct
method calls on VaultServiceHandle — no VaultProtocol enum, no
VaultMessage, no VaultServiceActor, no mpsc channel, no Service trait, no
RemoteService trait, no postcard serialization. The vault is local-only by
construction.

The core security argument: irpc made the vault remote-capable by default
(RemoteService generated unless no_rpc is passed). The IrohProtocol handler
forwards all messages without auth. The docs framed 'register an ALPN' as a
server-setup change. This is the default-insecure anti-pattern — security
should be opt-in, not opt-out. ADR-025 inverts the default: local-only is
the only mode, and remote access requires building a separate vault-server
crate (a visible architectural act, not a flag flip).

The actor path was already dead code — service.md said 'prefer
VaultServiceHandle directly — no channel, no serialization.' The actor
existed only to make irpc's Service trait work, which existed only to make
RemoteService work, which was the footgun. VaultServiceHandle's
Arc<RwLock> provides concurrent reads and exclusive writes — better
throughput than the actor's sequential processing.

DerivedKey serialization simplifies: always redact on serialize (for
logging safety), reject '[REDACTED]' on deserialize with an error. No
'postcard preserves bytes' path. This resolves review #002 W8 (silent
corruption on JSON-deserialized DerivedKey).

Resolves:
- OQ-21: remote vault access — resolved (not deferred). Not a vault crate
  feature; if needed, a separate vault-server crate with its own ADR.
- C7: vault-server-crate question decided — not created now, not precluded.
- C8: operation access policy table dissolved — all operations local-only
  by default; if a vault-server crate exposes some remotely, that crate
  defines the policy.
- W8: DerivedKey JSON deserialization — resolved (reject redacted payloads).

Amends ADR-005 (irpc remains for alknet-call, not for alknet-vault),
ADR-018 (vault is even more standalone — zero RPC framework deps),
ADR-019 (vault is the only layer, not just the only direct-caller layer),
ADR-008 (vault integration point unchanged, but now local-only by
construction).
2026-06-22 14:53:52 +00:00

4.1 KiB

ADR-005: irpc as Call Protocol Foundation

Status

Accepted

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

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-013)
  • 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

  • Pivot proposal: docs/research/pivot/alpn-service-architecture.md
  • ADR-003: Crate decomposition
  • ADR-004: Auth as shared core (IdentityProvider)
  • irpc reference: docs/research/references/iroh/irpc/ (see individual docs in that directory)
  • The previous architecture had an equivalent decision in ADR-024 (bidirectional call protocol with EventEnvelope framing), which is archived in the reference implementation at /workspace/@alkdev/alknet-main/.