docs(architecture): N-3 — registry types/schemas pinned (freeze-inventory draft)
- new backend.md §"Registry types and schemas": RepoRecord serde shape
(three-action grants per ADR-015, opaque grant keys, storage_root
omitted from all op responses per ADR-008); the five-variant
RegistryError set with wire mappings; the four git/repo/* op
request/response schemas with additionalProperties: false inputs and
the git:repo:* error-code namespace
- the not_found/forbidden collapse carries one wire code ('unauthorized')
per N-2's rule; AlreadyExists is create-side only (no existence oracle)
- OQ-03 inventory note + review 001 N-3 marked resolved — review 001 is
now 14/14
verification: cargo test, clippy -D warnings, fmt --check, doc,
publish --dry-run — clean
This commit is contained in:
1 parent
11ceead6fd
commit
b6040d36a9
3 files changed
+89
-2
No files matched your search
@@ -166,6 +166,89 @@ planned: call ops only):
|
||||
the single per-repo authz surface, and repo creation seeds the
|
||||
creator's `{read, write, manage}` grants (ADR-015).
|
||||
|
||||
## Registry types and schemas (review 001 N-3; the freeze-inventory draft)
|
||||
|
||||
The serde shapes the ops handlers and the `registry-file` store both
|
||||
serialize — pinned here so the two workstreams cannot invent them
|
||||
independently. These are the N-3 schemas ADR-015 chained after the
|
||||
manage-grant shape: they capture the three-action grant shape (ADR-015),
|
||||
the opaque grant-key rule (ADR-015 §7 / OQ-16), and the
|
||||
unknown-repo ≡ unauthorized rule at the op layer (ADR-008). Draft until
|
||||
first publish (additive fields have been added before publish before —
|
||||
ADR-016's params, ADR-017's API); at publish they enter OQ-03's
|
||||
inventory as compat surface.
|
||||
|
||||
### `RepoRecord` (serde shape)
|
||||
|
||||
```json
|
||||
{
|
||||
"repo_id": "alkdev/alkgit", // the registry id (string, non-empty)
|
||||
"storage_root": "/var/lib/alkgit/…", // server-side path, never on the wire (ADR-008)
|
||||
"visibility": "public", // "public" | "private" (lowercase)
|
||||
"grants": { // identity_id (opaque string, ADR-015 §7) → grant set
|
||||
"u-7731": ["read", "write", "manage"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `grants` values are arrays of the action strings `read` / `write` /
|
||||
`manage` (unique, lowercase; `manage` implies the lower actions per
|
||||
ADR-015 §2's monotonicity — stored sets need not be closed, the
|
||||
policy function evaluates that).
|
||||
- `storage_root` is the one field that must never round-trip through an
|
||||
op response (ops return record *data*, not paths — ADR-008's never-a-
|
||||
path rule; the get-op response schema omits it, the `resolve`
|
||||
trait surface keeps it). Keys are `snake_case`, no serde renames —
|
||||
the record file IS the serde shape (ADR-012 §2's on-disk form).
|
||||
- Additive-field rule: unknown fields are rejected, fail-closed, both
|
||||
on-disk (an unknown field in a stored record file is a version skew
|
||||
the operator must see, not silently dropped) and in op input schemas
|
||||
(`additionalProperties: false` — the same extension rule as
|
||||
ADR-016's open-op params; extensions replace, not accumulate, before
|
||||
publish).
|
||||
|
||||
### `RegistryError` (the variant set)
|
||||
|
||||
`thiserror` enum, five variants — every failure the trait family can
|
||||
produce, no catch-all:
|
||||
|
||||
| Variant | Meaning | Wire mapping |
|
||||
|---|---|---|
|
||||
| `NotFound` | repo id unresolved | collapses with `Forbidden` to the generic denial (unknown ≡ unauthorized, ADR-008; transport.md error taxonomy) |
|
||||
| `Forbidden` | gate denied (op-layer or `authorize`) | generic FORBIDDEN — same wire shape as `NotFound`, never a variant leak |
|
||||
| `AlreadyExists` | create with a taken id | op-level error; still NOT disclosed as an existence oracle to non-creators — `create` responses may carry it, `resolve` never returns it |
|
||||
| `Invalid` | malformed record/field (store-side validation, e.g. empty repo id, unknown action string) | op-level error with the field name |
|
||||
| `Io(String)` | backing-store failure (serialized as message — not `std::io::Error`, which is not serde-stable) | session/op error, substrate-appropriate |
|
||||
|
||||
`Io` is the only stringly-typed variant; everything else is structural
|
||||
so ops can report precise JSON errors (`{"error": "already_exists", …}`)
|
||||
while the trait surface stays `Result<_, RegistryError>` per ADR-012 §1.
|
||||
|
||||
### `git/repo/*` op JSON schemas (request/response)
|
||||
|
||||
| Op | Request | Response | Errors |
|
||||
|---|---|---|---|
|
||||
| `git/repo/create` | `{repo_id, visibility}` — storage root assigned by the store's naming rules (ADR-012 §2); grants seeded `{read, write, manage}` for the caller (ADR-015 §4); an explicit grants field is an admin-shape extension, NOT v1 | `{repo_id, visibility}` | `already_exists`, `invalid` |
|
||||
| `git/repo/delete` | `{repo_id}` | `{}` | `unauthorized` (the single wire code for unknown-repo ≡ not-authorized — N-2's collapse rule; `io`) |
|
||||
| `git/repo/update` | `{repo_id, visibility?, grants?}` — both optional, full-record replace of the provided fields; grant mutation is the only grant-write path (ADR-015 §3) | `{repo_id, visibility, grants}` (post-write record, no storage root) | `unauthorized` (collapsed, N-2), `invalid`, `io` |
|
||||
| `git/repo/get` | `{repo_id}` (single read; list is a v2 additive op) | `{repo_id, visibility, grants}` (no storage root — ADR-008) | `unauthorized` (collapsed, N-2), `io` |
|
||||
|
||||
- Error codes are the wire `code` strings in the table (snake_case,
|
||||
one namespace `git:repo:*`), each carried in the op's
|
||||
`ErrorDefinition` (alkcall ADR-016 disclosure convention — the same
|
||||
pattern alktty's `channel:open_failed` uses). The `unauthorized` code
|
||||
is the collapsed denial: both `RegistryError::NotFound` and
|
||||
`Forbidden` map to it at the op-wire boundary (unknown ≡
|
||||
unauthorized, ADR-008 + review 001 N-2), so no variant leaks an
|
||||
existence oracle; `AlreadyExists` is create-side only (the caller
|
||||
knows the id — discloseable without an oracle).
|
||||
- `update`'s response echoes the post-write record so grant edits
|
||||
confirm what landed (the last-writer-race posture is ADR-012
|
||||
§Consequences, unchanged).
|
||||
- All four op input schemas: `additionalProperties: false` (the
|
||||
alksocks `{}`-precedent — same fail-closed extension rule as
|
||||
ADR-016's open-op params).
|
||||
|
||||
## Design Decisions
|
||||
|
||||
| ADR | Decision | Summary |
|
||||
@@ -181,6 +264,7 @@ planned: call ops only):
|
||||
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | thin-pack ingestion, transaction CAS, report framing |
|
||||
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` |
|
||||
| [016](decisions/016-native-session-preamble.md) | Native session preamble | `{repo, service}` open-op params, request-line preamble, service in the tuple |
|
||||
| [017](decisions/017-consumer-half-git-session.md) | Consumer half | `GitSession` typed client (`ls_refs`/`fetch`/`push`), custom alkcall Transport + gix-protocol, hand-rolled push |
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
@@ -72,7 +72,10 @@ resolved in earlier cycles. OQ-16 is deferred but blocks nothing in v1
|
||||
trait surface (`GitPackIngest` binding, `GitPackGen::common_haves`);
|
||||
ADR-016 added the native-path preamble wire shapes (the `{repo,
|
||||
service}` open-op params schema and the git-daemon request-line
|
||||
grammar — the last native-path wire surface).
|
||||
grammar — the last native-path wire surface); ADR-017 added the
|
||||
`GitSession` public API; the registry types/schemas draft
|
||||
(backend.md §"Registry types and schemas", review 001 N-3) pinned
|
||||
`RepoRecord`, `RegistryError`, and the `git/repo/*` op schemas.
|
||||
- **Door type**: one-way (API freeze is registry-visible to dependents)
|
||||
- **Priority**: medium
|
||||
- **Impacts**: blocks the first publish only, not implementation.
|
||||
|
||||
Reference in new issue
Block a user