docs: spec-level sync with the single-publish fix + port-debt repair
The spec docs had not caught up with the stub-sweep fix commits (4eae18f..3317ea0), and the ported docs still carried alknet numbering that now collides with alkcall's own ADR range. Spec sync with the landed code: - call-protocol.md: register_imported/register_imported_all return Result and refuse marked specs; marked-spec registration refusal contract spelled out (ADR-047 am. 4 §4.8, ADR-022 am. 2026-10-13) - client-and-adapters.md: from_call flow gains the Pub sink-forwarding branch and the marked-op refusal (resolves OQ-37 — no from_call relay wrapper; sanctioned compositions are ChannelRelay::register_relay_openable / ChannelClient) - ADR-051: "channel/control is not implemented" stale sentence replaced with the ADR-052 per-leg control contract - OQ-37 resolved; OQ-22 split (primitive resolved; broker placement out of scope) with new OQ-42 for the downstream hub broker (deferred(scope), concretely blocked); OQ-24 resolved by the shipped add/strip surface; OQ-41 → deferred(scope) with blocking condition - README: OQ bullets renumbered to tracker IDs; alknet-collision note rewritten (range 001..053, alknet prefix convention); new principle 11 (implement-or-stop-advertising); AGENTS.md ADR range 001..053 Port-debt repair: - 149 broken decision links across 17 docs — ported with alknet numbers/paths, most now remapped to alkcall files by slug; the rest link the alknet repo per the README convention - stale OQ numbering rewritten to tracker IDs in call-protocol.md, call-README.md, operation-registry.md, client-and-adapters.md, ADR-025, channels-overview/README/wire, channel-client - unambiguous "alknet ADR-NNN" labels where an alknet number collides with an alkcall ADR (052 TTY vs channel/control, 033 storage vs rust-canonical, 025/026/027+020 vault vs PeerEntry/auth ADRs) Verified: cargo test (697 + guard), fmt, clippy --all-targets -D warnings; full markdown link scan clean.
This commit is contained in:
1 parent
3317ea0963
commit
7b8a3e7558
26 files changed
+432
-332
No files matched your search
@@ -40,7 +40,7 @@ This document specifies three components, all in `alknet-call`:
|
||||
> It is now an HTTP-backed single-endpoint adapter for non-standard /
|
||||
> non-OpenAPI / basic REST endpoints, functionally similar to
|
||||
> `from_openapi` but one endpoint at a time. See
|
||||
> [`crates/http/http-adapters.md`](../http/http-adapters.md) §"from_jsonschema".
|
||||
> [`crates/http/http-adapters.md`](/workspace/@alkdev/alknet/docs/architecture/crates/http/http-adapters.md) §"from_jsonschema".
|
||||
> The `FromJsonSchema` provenance variant stays in `alknet-call`
|
||||
> (`OperationProvenance`); only the adapter implementation moved.
|
||||
|
||||
@@ -80,8 +80,8 @@ primary use case) and that its parallel `remote_safe`/`trusted_peer`
|
||||
authorization system duplicated the existing `AccessControl`/`Identity`
|
||||
machinery. **ADR-024 supersedes ADR-023**: peer-keyed overlays + `PeerRef`
|
||||
routing, and peer authorization through the existing `AccessControl::check(peer_identity)`.
|
||||
DC-2/3/4 are two-way-door defaults recorded here (DC-2→OQ-27, DC-3→OQ-28
|
||||
cross-peer dissolved / same-peer stays, DC-4→OQ-26).
|
||||
DC-2/3/4 are two-way-door defaults recorded here (DC-2→OQ-12, DC-3→OQ-13
|
||||
cross-peer dissolved / same-peer stays, DC-4→OQ-11).
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -136,7 +136,7 @@ the op's required scopes/resources are satisfied, the call dispatches; if not,
|
||||
`FORBIDDEN` before the handler runs (capabilities never populated — the
|
||||
security property). An op that should never be callable from the wire uses
|
||||
`Visibility::Internal` (existing mechanism, `NOT_FOUND` before ACL). See
|
||||
[ADR-024](decisions/029-peer-graph-routing-model.md) §3 for the full
|
||||
[ADR-024](decisions/024-peer-graph-routing-model.md) §3 for the full
|
||||
mapping of the three `remote_safe` cases to `AccessControl`/`Visibility`.
|
||||
|
||||
The connection is symmetric after establishment (ADR-022 §2): both sides can
|
||||
@@ -212,7 +212,7 @@ pub type PeerId = String; // = Identity.id from IdentityProvider resolution
|
||||
|
||||
`OperationEnv` gains a peer-routing method with a `PeerRef` selector
|
||||
(`Specific(PeerId)` / `Any`), default-impl for back-compat. See
|
||||
[ADR-024](decisions/029-peer-graph-routing-model.md) §2 for the full
|
||||
[ADR-024](decisions/024-peer-graph-routing-model.md) §2 for the full
|
||||
`invoke_peer` signature and `ScopedPeerEnv` peer-qualified reachability. The
|
||||
per-`CallConnection` overlay stays flat (one connection = one peer); the
|
||||
peer-keying is at the aggregation layer (the head node's composition env).
|
||||
@@ -225,7 +225,7 @@ single `AccessControl`-filtered handler (no `peer_scoped` variant, no
|
||||
`remote_safe` filter — both retired by ADR-024). `services/list-peers`
|
||||
is the opt-in for peer-attributed re-export listing (each peer's
|
||||
sub-overlay listed with attribution, filtered by the calling peer's
|
||||
authorization). See [ADR-024](decisions/029-peer-graph-routing-model.md) §6.
|
||||
authorization). See [ADR-024](decisions/024-peer-graph-routing-model.md) §6.
|
||||
|
||||
### Credential sources for connections
|
||||
|
||||
@@ -286,7 +286,7 @@ There is no call-protocol credential bundle. The transport dimensions
|
||||
`alknet-core` per ADR-012.
|
||||
|
||||
- **TLS identity** — the local node's Ed25519 raw key (RFC 7250) or X.509 cert,
|
||||
derived from the vault at startup (ADR-020, ADR-026, ADR-027).
|
||||
derived from the vault at startup (alknet ADR-020, ADR-026, ADR-027).
|
||||
- **Auth token** — an opaque call-protocol-level token, decrypted from the
|
||||
vault or derived from a shared secret.
|
||||
- **Remote identity verification** — the expected fingerprint/cert of the
|
||||
@@ -301,7 +301,7 @@ invariant (below). The concrete shapes of `TlsIdentity`, `AuthToken`, and
|
||||
`RemoteIdentity` are implementation-detail two-way doors; the one-way
|
||||
constraints are that they come from `Capabilities`, not env vars (ADR-010).
|
||||
|
||||
**TLS client-auth presentation** (OQ-29 #1, wired): the client presents
|
||||
**TLS client-auth presentation** (OQ-14 #1, wired): the client presents
|
||||
its Ed25519 key as an RFC 7250 raw public key client cert — the client-side
|
||||
equivalent of the server's `RawKeyCertResolver`. This is **wired now**, not
|
||||
additive: it is what activates the `PeerEntry` fingerprint → `peer_id`
|
||||
@@ -310,13 +310,13 @@ peer graph doesn't populate for quinn connections — `PeerId` resolution
|
||||
fails because the server has no client cert to extract a fingerprint from.
|
||||
The iroh path already works (iroh uses RFC 7250 raw keys and exchanges
|
||||
Ed25519 public keys during the TLS handshake automatically); the gap was
|
||||
quinn-only, and OQ-29 #1 resolves it by replacing `with_no_client_auth()`
|
||||
quinn-only, and OQ-14 #1 resolves it by replacing `with_no_client_auth()`
|
||||
with presenting the key. The one-way constraint (credentials from
|
||||
`Capabilities`, not env vars, ADR-010) is unaffected — the `auth_token`
|
||||
dimension flows through the call-protocol `auth_token` payload field, not
|
||||
TLS, so the no-env-vars invariant holds independently of the TLS layer.
|
||||
|
||||
**Remote-identity verification** (OQ-29 #2, additive): verifying the
|
||||
**Remote-identity verification** (OQ-14 #2, additive): verifying the
|
||||
server's fingerprint against an expected value (`credentials.remote_identity`)
|
||||
is **additive** — the server-side fingerprint extraction is what matters for
|
||||
`PeerId`, not the client-side verification. The verifier for raw keys can
|
||||
@@ -324,7 +324,7 @@ start as "accept any, extract fingerprint" and add fingerprint-pinning later.
|
||||
This is a two-way-door remainder; the one-way constraint (credentials from
|
||||
`Capabilities`, not env vars) is unaffected.
|
||||
|
||||
**Server cert verifier selection** (OQ-29 #2 + ADR-034 §3): the client-side
|
||||
**Server cert verifier selection** (OQ-14 #2 + alknet ADR-034 §3): the client-side
|
||||
`ServerCertVerifier` is selected by whether the local node has a `PeerEntry`
|
||||
for the remote, not by key type alone. A pure-client
|
||||
connection to a **public X.509 endpoint** (no `PeerEntry` on the local
|
||||
@@ -339,7 +339,7 @@ with mixed Ed25519 + X.509 fingerprints) uses fingerprint pinning on
|
||||
both cert paths and does enter the peer graph. An unknown Ed25519
|
||||
raw-key remote fails closed (no CA to fall back to — raw-key remotes
|
||||
are always known peers). See
|
||||
[ADR-034](decisions/034-outgoing-only-x509-and-three-peer-roles.md)
|
||||
[ADR-034](/workspace/@alkdev/alknet/docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md)
|
||||
for the verifier selection rule and the three-role naming.
|
||||
|
||||
### from_call
|
||||
@@ -353,7 +353,7 @@ forwarding handlers. The discovery mechanism (`services/list` +
|
||||
```rust
|
||||
pub struct FromCallConfig {
|
||||
/// Namespace prefix applied to imported operation names. Optional —
|
||||
/// default no prefix. Collision on import is an error (DC-3, OQ-28),
|
||||
/// default no prefix. Collision on import is an error (DC-3, OQ-13),
|
||||
/// not last-wins.
|
||||
pub namespace_prefix: Option<String>,
|
||||
/// Optional filter — import only operations whose names match. None
|
||||
@@ -384,31 +384,50 @@ The flow (ADR-022 §3):
|
||||
sends `call.requested` via `CallConnection::call_with_payload()`, awaits
|
||||
the single `call.responded` (or `call.error`), returns the
|
||||
`ResponseEnvelope`.
|
||||
- `Subscription` → a `StreamingHandler` (registered as
|
||||
`HandlerKind::Stream`): calls `CallConnection::subscribe()`, which
|
||||
returns `impl Stream<Item = ResponseEnvelope>` (the client-side
|
||||
streaming path, already implemented), maps it to a
|
||||
`BoxStream<ResponseEnvelope>`. The remote stream flows end-to-end:
|
||||
each `call.responded` the remote sends becomes a stream item; the
|
||||
remote's `call.completed` ends the stream (→ wire `call.completed`);
|
||||
`call.aborted` drops the stream (cascade per ADR-020). No truncation,
|
||||
no first-value fallback — a `from_call`-imported subscription forwards
|
||||
the full remote stream.
|
||||
- `provenance: FromCall`, `composition_authority: None`, `scoped_env: None`
|
||||
(leaf — ADR-018).
|
||||
- `Subscription` → a `StreamingHandler` (registered as
|
||||
`HandlerKind::Stream`): calls `CallConnection::subscribe()`, which
|
||||
returns `impl Stream<Item = ResponseEnvelope>` (the client-side
|
||||
streaming path, already implemented), maps it to a
|
||||
`BoxStream<ResponseEnvelope>`. The remote stream flows end-to-end:
|
||||
each `call.responded` the remote sends becomes a stream item; the
|
||||
remote's `call.completed` ends the stream (→ wire `call.completed`);
|
||||
`call.aborted` drops the stream (cascade per ADR-020). No truncation,
|
||||
no first-value fallback — a `from_call`-imported subscription forwards
|
||||
the full remote stream.
|
||||
- `Pub` → the sink forwarding shape (registered as `HandlerKind::Sink`,
|
||||
ADR-046 + ADR-022 amendment 2026-10-13): a real forwarding body, not
|
||||
an advertised-always-failing stub. A local `publish()` on the
|
||||
imported op streams the `call.published` chunks across the
|
||||
connection; the remote peer's `SinkHandler` consumes them.
|
||||
- `provenance: FromCall`, `composition_authority: None`, `scoped_env: None`
|
||||
(leaf — ADR-018).
|
||||
4. The caller registers the bundles via
|
||||
`CallConnection::register_imported_all()`.
|
||||
|
||||
**Re-import on reconnection** (DC-2, OQ-27): `from_call` is a free function;
|
||||
**Marked ops are not importable** (ADR-047 amendment 4 §4.8):
|
||||
`from_call`'s discovery reconstructs the `channel_open` marker (ADR-047
|
||||
Gap F) so the importing side can *report* the remote channel-machinery
|
||||
ops, but `register_imported` / `register_imported_all` refuse marked
|
||||
specs — the marker's data plane is the allocated channel's `BiStream`,
|
||||
which has no overlay path (`OperationEnv::invoke` is request/response
|
||||
only, ADR-046 §6). A hub that needs a marked op composes it with the
|
||||
channel machinery instead: `ChannelRelay::register_relay_openable`
|
||||
(ADR-051) on the relaying side, or direct consumption via `ChannelClient`
|
||||
(ADR-043). Likewise `op/register` refuses a marked announcement with
|
||||
`MARKED_SPEC_UNSUPPORTED` (ADR-022 amendment 2026-10-13). This resolves
|
||||
OQ-37: there is no `from_call` relay wrapper for marked ops — the relay
|
||||
composition is the sanctioned path.
|
||||
|
||||
**Re-import on reconnection** (DC-2, OQ-12): `from_call` is a free function;
|
||||
the assembly layer calls it after the dial (in `AlknetClient`). The overlay
|
||||
is per-connection (Layer 2, ADR-019), so a stale overlay dies with the
|
||||
connection; re-import on reconnect is naturally scoped to the new
|
||||
connection. A `CallConnection::refresh()` method for mid-connection
|
||||
re-discovery is a genuine feature addition — non-breaking, additive — if a
|
||||
deployment needs manual re-discovery without drop-and-reconnect. See
|
||||
[ADR-028](decisions/069-from-call-manual-free-function.md).
|
||||
[ADR-028](decisions/028-from-call-manual-free-function.md).
|
||||
|
||||
**Namespace collision** (DC-3, OQ-28): under the peer-graph model (ADR-024),
|
||||
**Namespace collision** (DC-3, OQ-13): under the peer-graph model (ADR-024),
|
||||
cross-peer collision dissolves — same name on different peers is fine (they
|
||||
live in separate peer sub-overlays, no prefix needed). Same-peer collision
|
||||
stays an error (a peer shouldn't expose two ops with the same name).
|
||||
@@ -432,7 +451,7 @@ forwarding. The spoke receives it as metadata on its `OperationContext` —
|
||||
available for logging, auditing, per-user rate limiting, but never used by
|
||||
`AccessControl::check` (the spoke authorizes the hub, its direct caller,
|
||||
not the end user). The hub may set `forwarded_for: None` if it doesn't
|
||||
want to disclose the originator. See [ADR-026](decisions/032-forwarded-for-identity.md).
|
||||
want to disclose the originator. See [ADR-026](decisions/026-forwarded-for-identity.md).
|
||||
|
||||
### from_jsonschema
|
||||
|
||||
@@ -443,7 +462,7 @@ and the "schema-only, no handler" concept conflated schema validation
|
||||
(a planning activity that doesn't need a registry entry) with operation
|
||||
registration (which always needs a handler).
|
||||
|
||||
[ADR-027](decisions/066-from-jsonschema-as-http-adapter.md) moved
|
||||
[ADR-027](decisions/027-from-jsonschema-as-http-adapter.md) moved
|
||||
`from_jsonschema` to `alknet-http` as an HTTP-backed single-endpoint
|
||||
adapter: the caller supplies an `OperationSpec` + `HttpServiceConfig` +
|
||||
path template + method, and the adapter builds one
|
||||
@@ -451,7 +470,7 @@ path template + method, and the adapter builds one
|
||||
`FromJsonSchema` provenance. It is functionally similar to `from_openapi`
|
||||
but one endpoint at a time, for non-standard / non-OpenAPI / basic REST
|
||||
endpoints that don't have a full OpenAPI document. See
|
||||
[`crates/http/http-adapters.md`](../http/http-adapters.md) §"from_jsonschema".
|
||||
[`crates/http/http-adapters.md`](/workspace/@alkdev/alknet/docs/architecture/crates/http/http-adapters.md) §"from_jsonschema".
|
||||
|
||||
The schema-validation-without-a-handler use case (the original stated
|
||||
purpose) is served by consuming `OperationSpec` directly — the spec
|
||||
@@ -482,7 +501,7 @@ which may be QUIC, TCP+TLS, or any other transport). Sync adapters
|
||||
trait — their `import()` bodies contain no `.await` points. This is locked by
|
||||
ADR-022 §5.
|
||||
|
||||
The **error type** (DC-4, OQ-26) is `Result<Vec<HandlerRegistration>,
|
||||
The **error type** (DC-4, OQ-11) is `Result<Vec<HandlerRegistration>,
|
||||
AdapterError>` where `AdapterError` is a crate-level enum covering the
|
||||
failure modes real implementations hit: discovery transport failure
|
||||
(`from_call` remote unreachable), schema parse failure (`from_openapi`,
|
||||
@@ -711,14 +730,14 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
via `PeerRef::Specific` / `PeerRef::Any` (ADR-024 §1-2).
|
||||
- **`from_call` is a manual free function.** The assembly layer calls it
|
||||
after the dial (in `AlknetClient`). The overlay is per-connection so
|
||||
re-import on reconnect is naturally scoped (DC-2, OQ-27). See
|
||||
[ADR-028](decisions/069-from-call-manual-free-function.md).
|
||||
re-import on reconnect is naturally scoped (DC-2, OQ-12). See
|
||||
[ADR-028](decisions/028-from-call-manual-free-function.md).
|
||||
- **`from_call` namespace collision is same-peer only.** Cross-peer collision
|
||||
dissolves (same name on different peers is fine — separate sub-overlays,
|
||||
ADR-024 §5). Same-peer collision stays an error. `namespace_prefix` is
|
||||
optional local-naming sugar, not the disambiguation mechanism (DC-3, OQ-28).
|
||||
optional local-naming sugar, not the disambiguation mechanism (DC-3, OQ-13).
|
||||
- **`OperationAdapter::import()` returns `Result`.** Failures surface as
|
||||
`AdapterError` (DC-4, OQ-26).
|
||||
`AdapterError` (DC-4, OQ-11).
|
||||
- **MCP stdio transport is not built.** Streamable HTTP is the only supported
|
||||
MCP transport in alknet. stdio = spawn arbitrary executable = built-in RCE.
|
||||
Recorded as an explicit security position, not a feature gap.
|
||||
@@ -730,7 +749,7 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
invoked through the `CallConnection` handle. The client-side
|
||||
`ServerCertVerifier` uses CA verification (`WebPkiServerVerifier`) for
|
||||
such remotes; known peers (hub with `PeerEntry`) use fingerprint
|
||||
pinning. See [ADR-034](decisions/034-outgoing-only-x509-and-three-peer-roles.md).
|
||||
pinning. See [ADR-034](/workspace/@alkdev/alknet/docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md).
|
||||
- **`ConnectionCredentials.remote_identity: None` is load-bearing.** `None`
|
||||
means "no `PeerEntry` for this remote → use CA verification (X.509)
|
||||
or fail closed (Ed25519 raw key)" per the ADR-034 §3 verifier rule.
|
||||
@@ -743,79 +762,73 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
|
||||
| Decision | ADR | Summary |
|
||||
|----------|-----|---------|
|
||||
| Call protocol client and adapter contract | [ADR-022](decisions/017-call-protocol-client-and-adapter-contract.md) | `CallClient` opens connections; `from_call` imports remote ops; connection direction independent of call direction; trait is async; adapters produce `HandlerRegistration` bundles |
|
||||
| `from_jsonschema` as HTTP-backed single-endpoint adapter in alknet-http | [ADR-027](decisions/066-from-jsonschema-as-http-adapter.md) | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a leaf |
|
||||
| Peer-graph routing model (DC-1, supersedes ADR-023) | [ADR-024](decisions/029-peer-graph-routing-model.md) | Peer-keyed overlays + `PeerRef` routing; peer authorization via existing `AccessControl::check(peer_identity)`; retires `remote_safe`/`trusted_peer` |
|
||||
| PeerEntry and Identity.id decoupling | [ADR-025](decisions/030-peerentry-and-identity-id-decoupling.md) | `PeerId` source changes from UUID to `Identity.id` (= `PeerEntry.peer_id`, stable across key rotation); `Identity.id` decoupled from crypto material on the fingerprint path |
|
||||
| Forwarded-for identity | [ADR-026](decisions/032-forwarded-for-identity.md) | `forwarded_for` field on `call.requested` and `OperationContext`; the `from_call` handler populates it; metadata only, never used by `AccessControl::check` |
|
||||
| Storage boundary and repo/adapter pattern | [ADR-033](decisions/033-storage-boundary-and-repo-adapter-pattern.md) | Core defines repo traits + in-memory defaults; persistence adapters are separate crates |
|
||||
| Secret material flow and capability injection | [ADR-010](decisions/014-secret-material-flow-and-capability-injection.md) | The no-env-vars invariant's foundation; capabilities injected at assembly layer |
|
||||
| Handler registration, provenance, and composition authority | [ADR-018](decisions/022-handler-registration-provenance-and-composition-authority.md) | The registration bundle adapters produce; `composition_authority: None` for leaves |
|
||||
| Operation registry layering | [ADR-019](decisions/024-operation-registry-layering.md) | Layer 2 per-connection overlay where `from_call` imports land |
|
||||
| Privilege model and authority context | [ADR-017](decisions/015-privilege-model-and-authority-context.md) | Adapter-registered ops are `Internal` by default; default-deny posture |
|
||||
| Abort cascade for nested calls | [ADR-020](decisions/016-abort-cascade-for-nested-calls.md) | Cross-node abort through `from_call` forwarding handler's `parent_request_id` |
|
||||
| Operation error schemas | [ADR-016](decisions/023-operation-error-schemas.md) | `error_schemas` mirrored by `from_call` from remote op's spec |
|
||||
| Streaming handler for subscriptions | [ADR-021](decisions/049-streaming-handler-for-subscriptions.md) | `from_call` `Subscription` ops register a `StreamingHandler` (`HandlerKind::Stream`) that calls `CallConnection::subscribe()` and forwards the remote stream; `Query`/`Mutation` stay `HandlerKind::Once` |
|
||||
| TLS identity redesign | [ADR-027](decisions/027-tls-identity-redesign-acme-rawkey-decoupling.md) | RFC 7250 raw key / X.509 cert dimensions of the local `TlsIdentity` (now carried by `ConnectionCredentials.local_identity`) |
|
||||
| Outgoing-only X.509 and three peer roles | [ADR-034](decisions/034-outgoing-only-x509-and-three-peer-roles.md) | Public X.509 endpoint is not a `PeerEntry` on the client side (no `PeerId`, not in peer graph); client-side verifier by `PeerEntry` presence (CA vs fingerprint pin); hub = mixed-fingerprint `PeerEntry` |
|
||||
| HD derivation for encryption keys | [ADR-020](decisions/020-hd-derivation-for-encryption-keys.md) | Vault-derived TLS identity material |
|
||||
| Vault key model | [ADR-026](decisions/026-vault-key-model-hd-derivation.md) | Vault-derived TLS identity material |
|
||||
| Vault local-only dispatch | [ADR-025](decisions/025-vault-local-only-dispatch.md) | Vault access at assembly layer only; the credential injection path's first hop |
|
||||
| Crate decomposition | [ADR-031](decisions/003-crate-decomposition.md) | `alknet-http` depends on `alknet-call` (protocol-foundation exception, noted in Adapter Location Map) |
|
||||
| One-way door decision framework | [ADR-032](decisions/009-one-way-door-decision-framework.md) | Door-type classification for DC-1..4 |
|
||||
| Call protocol client and adapter contract | [ADR-022](decisions/022-call-protocol-client-and-adapter-contract.md) | `CallClient` opens connections; `from_call` imports remote ops; connection direction independent of call direction; trait is async; adapters produce `HandlerRegistration` bundles |
|
||||
| `from_jsonschema` as HTTP-backed single-endpoint adapter in alknet-http | [ADR-027](decisions/027-from-jsonschema-as-http-adapter.md) | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a leaf |
|
||||
| Peer-graph routing model (DC-1, supersedes ADR-023) | [ADR-024](decisions/024-peer-graph-routing-model.md) | Peer-keyed overlays + `PeerRef` routing; peer authorization via existing `AccessControl::check(peer_identity)`; retires `remote_safe`/`trusted_peer` |
|
||||
| PeerEntry and Identity.id decoupling | [ADR-025](decisions/025-peerentry-and-identity-id-decoupling.md) | `PeerId` source changes from UUID to `Identity.id` (= `PeerEntry.peer_id`, stable across key rotation); `Identity.id` decoupled from crypto material on the fingerprint path |
|
||||
| Forwarded-for identity | [ADR-026](decisions/026-forwarded-for-identity.md) | `forwarded_for` field on `call.requested` and `OperationContext`; the `from_call` handler populates it; metadata only, never used by `AccessControl::check` |
|
||||
| Storage boundary and repo/adapter pattern | [alknet ADR-033](/workspace/@alkdev/alknet/docs/architecture/decisions/033-storage-boundary-and-repo-adapter-pattern.md) | Core defines repo traits + in-memory defaults; persistence adapters are separate crates |
|
||||
| Secret material flow and capability injection | [ADR-010](decisions/010-secret-material-flow-and-capability-injection.md) | The no-env-vars invariant's foundation; capabilities injected at assembly layer |
|
||||
| Handler registration, provenance, and composition authority | [ADR-018](decisions/018-handler-registration-provenance-and-composition-authority.md) | The registration bundle adapters produce; `composition_authority: None` for leaves |
|
||||
| Operation registry layering | [ADR-019](decisions/019-operation-registry-layering.md) | Layer 2 per-connection overlay where `from_call` imports land |
|
||||
| Privilege model and authority context | [ADR-017](decisions/017-privilege-model-and-authority-context.md) | Adapter-registered ops are `Internal` by default; default-deny posture |
|
||||
| Abort cascade for nested calls | [ADR-020](decisions/020-abort-cascade-for-nested-calls.md) | Cross-node abort through `from_call` forwarding handler's `parent_request_id` |
|
||||
| Operation error schemas | [ADR-016](decisions/016-operation-error-schemas.md) | `error_schemas` mirrored by `from_call` from remote op's spec |
|
||||
| Streaming handler for subscriptions | [ADR-021](decisions/021-streaming-handler-for-subscriptions.md) | `from_call` `Subscription` ops register a `StreamingHandler` (`HandlerKind::Stream`) that calls `CallConnection::subscribe()` and forwards the remote stream; `Query`/`Mutation` stay `HandlerKind::Once` |
|
||||
| TLS identity redesign | [alknet ADR-027](/workspace/@alkdev/alknet/docs/architecture/decisions/027-tls-identity-redesign-acme-rawkey-decoupling.md) | RFC 7250 raw key / X.509 cert dimensions of the local `TlsIdentity` (now carried by `ConnectionCredentials.local_identity`) |
|
||||
| Outgoing-only X.509 and three peer roles | [alknet ADR-034](/workspace/@alkdev/alknet/docs/architecture/decisions/034-outgoing-only-x509-and-three-peer-roles.md) | Public X.509 endpoint is not a `PeerEntry` on the client side (no `PeerId`, not in peer graph); client-side verifier by `PeerEntry` presence (CA vs fingerprint pin); hub = mixed-fingerprint `PeerEntry` |
|
||||
| HD derivation for encryption keys | [alknet ADR-020](/workspace/@alkdev/alknet/docs/architecture/decisions/020-hd-derivation-for-encryption-keys.md) | Vault-derived TLS identity material |
|
||||
| Vault key model | [alknet ADR-026](/workspace/@alkdev/alknet/docs/architecture/decisions/026-vault-key-model-hd-derivation.md) | Vault-derived TLS identity material |
|
||||
| Vault local-only dispatch | [alknet ADR-025](/workspace/@alkdev/alknet/docs/architecture/decisions/025-vault-local-only-dispatch.md) | Vault access at assembly layer only; the credential injection path's first hop |
|
||||
| Crate decomposition | [ADR-031](decisions/031-crate-decomposition.md) | `alknet-http` depends on `alknet-call` (protocol-foundation exception, noted in Adapter Location Map) |
|
||||
| One-way door decision framework | [ADR-032](decisions/032-one-way-door-decision-framework.md) | Door-type classification for DC-1..4 |
|
||||
|
||||
## Open Questions
|
||||
|
||||
See [open-questions.md](open-questions.md) for full details.
|
||||
|
||||
- **OQ-25** (dissolved by ADR-024): `remote_safe` marking shape — moot.
|
||||
- **OQ-10** (dissolved by ADR-024): `remote_safe` marking shape — moot.
|
||||
`remote_safe`/`trusted_peer` are retired; peer authorization is
|
||||
`AccessControl::check(peer_identity)`. No marking to shape.
|
||||
- **OQ-26** (resolved): `AdapterError` variants — `DiscoveryFailed`,
|
||||
- **OQ-11** (resolved): `AdapterError` variants — `DiscoveryFailed`,
|
||||
`SchemaParse`, `Transport`, `Unauthorized`, `SamePeerCollision`
|
||||
(replaces flat `Conflict`). `#[non_exhaustive]`.
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a
|
||||
- **OQ-12** (resolved): `from_call` re-import trigger — `from_call` is a
|
||||
manual free function; the assembly layer calls it after the dial (in
|
||||
`AlknetClient`). A `CallConnection::refresh()` method is a genuine
|
||||
feature addition — non-breaking, additive. See
|
||||
[ADR-028](decisions/069-from-call-manual-free-function.md).
|
||||
- **OQ-28** (resolved): `from_call` namespace collision — same-peer
|
||||
[ADR-028](decisions/028-from-call-manual-free-function.md).
|
||||
- **OQ-13** (resolved): `from_call` namespace collision — same-peer
|
||||
collision = error; cross-peer dissolved by ADR-024 (separate sub-overlays).
|
||||
`namespace_prefix` is optional local-naming sugar.
|
||||
- **OQ-29** (resolved): `CallClient` TLS client-auth — wire quinn
|
||||
- **OQ-14** (resolved): `CallClient` TLS client-auth — wire quinn
|
||||
client-auth (present Ed25519 key as raw public key client cert);
|
||||
key-type-aware server cert verification (raw key = fingerprint match,
|
||||
X.509 = CA verification); fingerprint normalization (`ed25519:` across
|
||||
quinn/iroh). The iroh path already works; the gap was quinn-only.
|
||||
See OQ-29 in open-questions.md.
|
||||
- **OQ-30** (resolved): `PeerRef::Any` routing policy — insertion-order
|
||||
See OQ-14 in open-questions.md.
|
||||
- **OQ-15** (resolved): `PeerRef::Any` routing policy — insertion-order
|
||||
first-match. A richer `RoutingPolicy` is a feature extension.
|
||||
- **OQ-31** (resolved): `services/list-peers` — opt-in; `services/list`
|
||||
- **OQ-16** (resolved): `services/list-peers` — opt-in; `services/list`
|
||||
is "own ops only."
|
||||
- **OQ-32** (open, feature extension): Multi-hop federation — the one-hop
|
||||
- **OQ-17** (deferred(scope), feature extension): Multi-hop federation — the one-hop
|
||||
model is the architectural commitment; multi-hop is a feature extension
|
||||
that doesn't break downstream. The peer-keyed model extends to multi-hop
|
||||
without redesign; petgraph is the candidate if path-finding becomes real
|
||||
(ADR-024 §3.7).
|
||||
- **OQ-33** (resolved by ADR-025): `PeerId` is a logical id. Source is
|
||||
- **OQ-18** (resolved by ADR-025): `PeerId` is a logical id. Source is
|
||||
`Identity.id` from `IdentityProvider` resolution (= `PeerEntry.peer_id`,
|
||||
stable across key rotation). See OQ-33 in open-questions.md.
|
||||
- **OQ-34** (resolved by ADR-025 + ADR-033): Persistent peer registry —
|
||||
stable across key rotation). See OQ-18 in open-questions.md.
|
||||
- **OQ-19** (resolved by ADR-025): Persistent peer registry —
|
||||
the storage boundary is `core trait + in-memory default` (config-backed
|
||||
`ConfigIdentityProvider` now; persistence adapters additive in separate
|
||||
crates). See OQ-34 in open-questions.md.
|
||||
- **OQ-35** (dissolved): the "API key asymmetry" framing was wrong;
|
||||
crates). See OQ-19 in open-questions.md.
|
||||
- **OQ-20** (dissolved): the "API key asymmetry" framing was wrong;
|
||||
`PeerEntry` supports multiple credential paths (fingerprints +
|
||||
auth_token_hash), `ApiKeyEntry` is for tokens that ARE the identity.
|
||||
See OQ-35 in open-questions.md.
|
||||
- **OQ-36** (resolved by ADR-035): Concrete persistence adapter shapes —
|
||||
read-sync / write-async split (`IdentityStore` async write trait
|
||||
extends the sync `IdentityProvider` read trait); SQLite adapter caches
|
||||
in memory and uses honker NOTIFY/LISTEN for no-restart cache
|
||||
invalidation; `alknet-store-sqlite` crate implements both
|
||||
`IdentityStore` and `CredentialStore`. See ADR-035 and OQ-36 in
|
||||
open-questions.md.
|
||||
- **OQ-37** (resolved by ADR-034): X.509 outgoing-only case — three
|
||||
See OQ-20 in open-questions.md.
|
||||
- **OQ-21** (resolved, alknet ADR-034 — not ported): X.509 outgoing-only
|
||||
case — three
|
||||
remote roles named (public X.509 endpoint, transport relay, hub).
|
||||
`PeerEntry` asymmetry is correct: a pure-client connection to a public
|
||||
X.509 endpoint is **not** in the call-protocol peer graph on the
|
||||
@@ -824,8 +837,10 @@ See [open-questions.md](open-questions.md) for full details.
|
||||
land in the connection's Layer 2 overlay and are invoked through the
|
||||
connection handle. The client-side `ServerCertVerifier` is selected
|
||||
by `PeerEntry` presence: known peer → fingerprint pin; unknown X.509
|
||||
remote → CA verification (`WebPkiServerVerifier`). See ADR-034 and
|
||||
OQ-37 in open-questions.md.
|
||||
remote → CA verification (`WebPkiServerVerifier`). The deciding ADR
|
||||
(alknet ADR-034, "Outgoing-Only X.509 and Three Peer Roles") was not
|
||||
ported into alkcall; the surviving alkcall surface is ADR-025's
|
||||
`PeerEntry` asymmetry. See OQ-21 in open-questions.md.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
Reference in new issue
Block a user