Public Access
Add ADRs 0001-0006 and README (M0)
Records the decisions the milestone plan already made, with their costs stated rather than only their benefits: - 0001 e2ee-trust-model: key hierarchy, the AAD-to-row binding that stops the server moving ciphertext between rows, and the four-layer public-key trust story. States plainly that revocation is not retroactive, that Connect cannot be a security boundary, and that the IdP becomes a key-distribution trust root. - 0002 minimal-apis: feature modules with explicit registration; capability negotiation instead of Asp.Versioning, since client and server upgrade independently when self-hosted. - 0003 sync-protocol: single write path, revision cursors, and the bigserial pre-commit sequence gap that silently corrupts sync — plus the per-vault advisory lock that fixes it and the test that must prove it. - 0004 relay-authorization: relay forwards bytes rather than terminating SSH, so zero-knowledge survives; server-resolved target IPs in the ticket to defeat DNS rebinding; why host addresses must be plaintext when relay is enabled. - 0005 no-application-layer: why the usual Application/mediator layer earns nothing here, with the trigger that would make us revisit it. - 0006 observability-stack: OTel plus built-in ILogger; liveness excludes dependencies so a database blip cannot restart the container and kill live SSH sessions. Also adds a README covering layout, build, enforced conventions and milestones.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# ADR 0002 — Minimal APIs with explicitly registered feature modules
|
||||
|
||||
- Status: accepted
|
||||
- Date: 2026-07-28
|
||||
|
||||
## Context
|
||||
|
||||
The API is roughly 60 endpoints: identity and enrollment, a public-key directory, teams,
|
||||
vaults and grants, sync, read-only item queries, relay, audit and admin. It is consumed by
|
||||
one first-party desktop client.
|
||||
|
||||
## Decision
|
||||
|
||||
Minimal APIs, grouped into feature modules under `Features/`, registered **explicitly** from
|
||||
`Setup/EndpointRegistration.cs`. Typed results (`Results<Ok<T>, ForbidHttpResult,
|
||||
ProblemHttpResult>`) throughout, one endpoint per file.
|
||||
|
||||
Version with a hard-coded `/api/v1` prefix and **no `Asp.Versioning` package**. Instead ship
|
||||
capability negotiation:
|
||||
|
||||
- `GET /api/v1/meta` — server version, sync protocol version, crypto spec version, feature
|
||||
flags, `minClientVersion`, and the push caps.
|
||||
- `GET /.well-known/dodossh-configuration` — OIDC authority, client id, scopes, relay URL.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Per-endpoint filters and metadata compose cleanly, and typed results give an accurate
|
||||
OpenAPI document without attribute noise.
|
||||
- Explicit registration over reflection scanning: predictable startup cost, trimming-friendly,
|
||||
and every route is greppable. The cost is one line per module, which is worth paying.
|
||||
- Minimal APIs' real weakness is that a cross-cutting concern is easy to forget. Two
|
||||
mitigations, both required: group-level `RequireAuthorization`, and an **endpoint-inventory
|
||||
test** that enumerates `EndpointDataSource` and fails the build if any endpoint is absent
|
||||
from an explicit authorization expectations table. A new endpoint therefore cannot be added
|
||||
without making an authorization decision.
|
||||
- Capability negotiation over version routing is the right trade for a self-hosted product,
|
||||
where client and server upgrade independently and skew is normal rather than exceptional.
|
||||
`.well-known` also *is* the onboarding story: the user types one server URL and the client
|
||||
discovers the rest. `Asp.Versioning` gets added when a v2 actually exists, not before.
|
||||
- The generated OpenAPI document is for third parties and a future CLI. It is emitted at build
|
||||
time to `artifacts/openapi/v1.json`, committed and CI-diffed. **The client's real contract is
|
||||
the `DodoSSH.Contracts` assembly**, guarded by PublicApiAnalyzers so an accidental DTO change
|
||||
is a build error rather than a runtime deserialisation failure on a user's laptop.
|
||||
|
||||
### Rejected
|
||||
|
||||
- **MVC controllers.** Would work, but bring filter/model-binding machinery this API does not
|
||||
need and blur the one-endpoint-one-file structure that keeps the authz surface reviewable.
|
||||
- **Reflection-based endpoint discovery.** Convenient until a route silently disappears
|
||||
because an assembly was not loaded, or trimming removes it.
|
||||
Reference in New Issue
Block a user