# Design Decisions Overview

This section collects the Architecture Decision Records (ADRs) that document the key design choices in quicprochat. Each ADR follows a standard format: context (why the decision was needed), decision (what was chosen), and consequences (trade-offs, benefits, and residual risks).

These decisions are not immutable. Each ADR has a status field and can be superseded by a later ADR if circumstances change. The goal is to preserve the reasoning behind each choice so that future contributors understand *why* the system works the way it does, not just *how*.

---

## ADR index

| ADR | Title | Status | One-line summary |
|---|---|---|---|
| [ADR-002](adr-002-capnproto.md) | Cap'n Proto over MessagePack (v1) | Superseded | Zero-copy, schema-enforced serialisation with built-in async RPC replaced hand-rolled MessagePack dispatch. Superseded by ADR-007. |
| [ADR-004](adr-004-mls-unaware-ds.md) | MLS-Unaware Delivery Service | Accepted | The DS routes opaque blobs by recipient key; it never inspects MLS content. |
| [ADR-005](adr-005-single-use-keypackages.md) | Single-Use KeyPackages | Accepted | The AS atomically removes a KeyPackage on fetch to preserve MLS forward secrecy. |
| [ADR-007](adr-007-protobuf-migration.md) | v1 Cap'n Proto to v2 Protobuf Migration | Accepted | Replace Cap'n Proto RPC with custom Protobuf framing over QUIC streams for better ecosystem support, 44-method surface, and multi-threaded dispatch. |

---

## Design comparison

For a broader comparison of quicprochat's design against alternative messaging protocols (Signal, Matrix/Olm/Megolm), see [Why This Design, Not Signal/Matrix/...](why-not-signal.md).

---

## How to read an ADR

Each ADR page follows this structure:

1. **Status** -- One of: Proposed, Accepted, Deprecated, Superseded. All current ADRs are Accepted unless noted.
2. **Context** -- The problem or force that motivated the decision. What constraints existed? What alternatives were considered?
3. **Decision** -- The specific choice that was made. What was selected and what was rejected?
4. **Consequences** -- The trade-offs that result from the decision. What are the benefits? What are the costs? What residual risks remain?
5. **Code references** -- Links to the source files where the decision is implemented.

---

## ADR-007: v1 Cap'n Proto to v2 Protobuf Migration

**Status**: Accepted

**Context**

quicprochat v1 used Cap'n Proto for both serialisation and RPC dispatch via
`capnp-rpc`. This worked well for the initial 8-method `NodeService` interface
but had several limitations as the protocol expanded:

- **`!Send` constraint**: `capnp-rpc` uses `Rc<RefCell<>>` internally, requiring
  all RPC futures to run on a `tokio::task::LocalSet`. This prevented multi-threaded
  dispatch and added complexity to every connection handler.
- **Schema growth friction**: Cap'n Proto's capability-based RPC model does not
  map cleanly to large flat method tables. Adding the 36 new methods (keys,
  blob, device, federation, moderation, recovery, etc.) would have required
  significant schema refactoring.
- **ALPN collision**: The `b"capnp"` ALPN identifier is not registered and could
  conflict with other Cap'n Proto deployments. A project-specific ALPN is cleaner.
- **Tooling**: `capnpc` requires a system-wide binary installation or a vendored
  copy. `prost-build` with `protobuf-src` self-vendors `protoc`, eliminating the
  build-time dependency.

**Decision**

Replace `capnp-rpc` with a custom binary framing layer (`quicprochat-rpc`) and
Protocol Buffers (`prost`) for payload serialisation:

- Three frame types: Request (10-byte header), Response (9-byte header), Push
  (6-byte header), all carrying Protobuf-encoded payloads.
- Method IDs are numeric `u16` constants dispatched via a handler registry.
  One QUIC bidirectional stream per RPC call; push events on QUIC uni-streams.
- ALPN changed from `b"capnp"` to `b"qpc"`. Default port changed from 7000 to 5001.
- Cap'n Proto legacy types are retained in `quicprochat-proto` for v1 compatibility
  but are no longer used for RPC dispatch.

**Consequences**

Benefits:
- Full `tokio::spawn` concurrency (no `LocalSet` required).
- 44-method RPC surface with clean numeric namespace and room to grow.
- Self-contained build (no system `protoc` dependency).
- Lighter middleware integration via Tower `Service` traits.
- Push event delivery without polling.

Costs:
- Lost Cap'n Proto zero-copy reads (Protobuf requires deserialisation). Acceptable
  because the hot path in the Delivery Service works with opaque `bytes::Bytes`
  without deserialisation.
- Lost promise pipelining from `capnp-rpc`. Not required for the current RPC
  surface; can be re-added with a future streaming RPC design.
- v1 clients are no longer wire-compatible with v2 servers.

**Code references**

- Frame format: `crates/quicprochat-rpc/src/framing.rs`
- Method IDs: `crates/quicprochat-proto/src/lib.rs` (`method_ids` module)
- Proto schemas: `proto/qpc/v1/*.proto`

---

## Cross-cutting themes

### Layered security

The core principle is that **no single layer is trusted alone**. QUIC/TLS transport encryption protects metadata and provides server authentication; MLS provides end-to-end content encryption with forward secrecy and post-compromise security; OPAQUE ensures the server never learns the user's password.

### Server minimalism

ADR-004 and ADR-005 reflect a design philosophy where the server does as little as possible. The DS does not parse MLS messages. The AS enforces single-use semantics through atomic removal rather than complex state tracking. This minimalism reduces the server's attack surface and makes it easier to audit.

### Schema-first design

The v2 protocol defines all messages and method IDs in checked-in source files
(`proto/qpc/v1/*.proto` and `crates/quicprochat-proto/src/lib.rs`). Every wire
type is documented, versioned, and evolvable through the standard Protobuf
schema evolution rules (adding optional fields, reserving removed field numbers).

---

## Further reading

- [Why This Design, Not Signal/Matrix/...](why-not-signal.md) -- comparative analysis against alternative protocols
- [Wire Format Reference](../wire-format/overview.md) -- the serialisation pipeline that implements these decisions
- [Architecture Overview](../architecture/overview.md) -- system-level view
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how the protocol layers stack
