docs(architecture): ADR-015 — manage grant tier + repo-op gate, OQ-16 identity namespace
Resolves review 001 finding A-1 (critical): ADR-012 §3's "scope
git:admin OR ownership" gate is not expressible in alkcall's
AccessControl (AND-composition). Resolution is the review's option (a)
shape with the OR-term generalized: the per-repo grant action set gains
manage, authorize(record, identity, read|write|manage) becomes the
single policy function for git access and repo administration, and the
delete/update/get gate is admin scope OR manage grant (handler-side,
generic FORBIDDEN, unknown-repo = unauthorized per ADR-008). Repo
create seeds the creator's {read, write, manage} grants —
administration is grantable, so collaborators/bots/app-compiled roles
work without global scopes. Ownership stays as alkcall spawn-tracking
(mint at create unchanged); "ownership never implies git access" is
superseded.
- ADR-015 (new): manage grant tier, op gate, flat-grants-as-replication-
substrate, opaque grant-key rule
- ADR-011: action set + policy domain amended, references updated
- ADR-012 §3: gate table replaced, two-tier paragraph superseded
- backend.md/doors.md/overview.md: gate + grant restatements, ADR tables
- OQ-16 (new, deferred(scope)): grant-key identity namespace —
globally-comparable ids for cross-assembly/replicator grant state;
tracker task tasks/architecture/oq-16-grant-identity-namespace.md
- review 001: A-1 marked resolved (ADR-015)
- vision.md: supersession notes (Internal-ops framing, v1 grant set)
Verification: cargo test, clippy -D warnings, fmt --check, doc --no-deps
all clean.
This commit is contained in:
1 parent
d478361a15
commit
c4b9c53674
11 files changed
+381
-64
No files matched your search
@@ -20,8 +20,11 @@ against real git 2.43. Previous cycles settled the auth/backend theme
|
|||||||
real-client captures: the receive-pack push state machine (ADR-013,
|
real-client captures: the receive-pack push state machine (ADR-013,
|
||||||
OQ-04) and the V2 multi-round negotiation ack loop (ADR-014, OQ-02). All
|
OQ-04) and the V2 multi-round negotiation ack loop (ADR-014, OQ-02). All
|
||||||
wire-layer design is now capture-grounded; the remaining open questions
|
wire-layer design is now capture-grounded; the remaining open questions
|
||||||
are the publish-freeze timing (OQ-03, a release decision) and sha256
|
are the publish-freeze timing (OQ-03, a release decision), sha256
|
||||||
policy (OQ-05, deferred on ecosystem need).
|
policy (OQ-05, deferred on ecosystem need), and the grant-key identity
|
||||||
|
namespace (OQ-16, deferred on the first cross-assembly deployment —
|
||||||
|
blocks nothing in v1). ADR-015 resolved
|
||||||
|
review 001's A-1 (the repo-op gate) with the `manage` grant tier.
|
||||||
|
|
||||||
## Architecture Documents
|
## Architecture Documents
|
||||||
|
|
||||||
@@ -51,13 +54,16 @@ policy (OQ-05, deferred on ecosystem need).
|
|||||||
| [012](decisions/012-registry-backing-and-ops.md) | Registry backing, write surface, CRUD ops, feature split | Accepted |
|
| [012](decisions/012-registry-backing-and-ops.md) | Registry backing, write surface, CRUD ops, feature split | Accepted |
|
||||||
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack state machine (V0-framed push, thin-pack, unpack-first CAS) | Accepted |
|
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack state machine (V0-framed push, thin-pack, unpack-first CAS) | Accepted |
|
||||||
| [014](decisions/014-v2-negotiation-ack-loop.md) | V2 negotiation ack loop (no `ready`, wait-for-done stays) | Accepted |
|
| [014](decisions/014-v2-negotiation-ack-loop.md) | V2 negotiation ack loop (no `ready`, wait-for-done stays) | Accepted |
|
||||||
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant tier + repo-op gate (admin scope OR manage grant) | Accepted |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
All unresolved questions are tracked in [open-questions.md](open-questions.md)
|
All unresolved questions are tracked in [open-questions.md](open-questions.md)
|
||||||
with stable OQ-IDs, priorities, and cross-references. Remaining: OQ-03
|
with stable OQ-IDs, priorities, and cross-references. Remaining: OQ-03
|
||||||
(publish freeze inventory — partially resolved, blocked on first-publish
|
(publish freeze inventory — partially resolved, blocked on first-publish
|
||||||
timing) and OQ-05 (sha256, deferred on ecosystem need). The wire-layer
|
timing), OQ-05 (sha256, deferred on ecosystem need), and OQ-16 (grant-key
|
||||||
|
identity namespace, deferred on the first cross-assembly record-sharing
|
||||||
|
deployment). The wire-layer
|
||||||
questions (OQ-02, OQ-04) resolved this cycle with ADR-014/ADR-013.
|
questions (OQ-02, OQ-04) resolved this cycle with ADR-014/ADR-013.
|
||||||
|
|
||||||
## Document Lifecycle
|
## Document Lifecycle
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ Five traits, kept small and orthogonal — the protocol crate never sees
|
|||||||
gix types:
|
gix types:
|
||||||
|
|
||||||
1. **`GitRegistry`** — repo id → `RepoRecord` (storage root, visibility,
|
1. **`GitRegistry`** — repo id → `RepoRecord` (storage root, visibility,
|
||||||
grants keyed on the stable logical identity id — ADR-011). Async
|
grants keyed on the stable logical identity id, action set
|
||||||
|
`{read, write, manage}` — ADR-011, ADR-015). Async
|
||||||
`resolve`; the authoritative mapping (ADR-008); resolution failure is
|
`resolve`; the authoritative mapping (ADR-008); resolution failure is
|
||||||
indistinguishable from authorization failure (ADR-007). Authorization
|
indistinguishable from authorization failure (ADR-007). Authorization
|
||||||
is alkgit-core's policy function evaluated on the record (data in the
|
is alkgit-core's policy function evaluated on the record (data in the
|
||||||
@@ -30,7 +31,9 @@ gix types:
|
|||||||
(`put_repo`/`update_repo`/`remove_repo`; alknet ADR-035's read/write
|
(`put_repo`/`update_repo`/`remove_repo`; alknet ADR-035's read/write
|
||||||
split shape). The `git/repo/*` ops wrap it (ADR-012 §3). Grants ride
|
split shape). The `git/repo/*` ops wrap it (ADR-012 §3). Grants ride
|
||||||
the record, so grant mutation is `update_repo` (convenience wrappers
|
the record, so grant mutation is `update_repo` (convenience wrappers
|
||||||
additive later, two-way).
|
additive later, two-way) — which is also the app's promotion path
|
||||||
|
(roles/teams compile to flat grants upstream; ADR-015 §6) and the
|
||||||
|
shape a future replication sync would write through (ADR-015 §8).
|
||||||
3. **`GitRefs`** — listing for advertisement (refs + peeled tags + symref
|
3. **`GitRefs`** — listing for advertisement (refs + peeled tags + symref
|
||||||
targets, the ls-refs response data) and ref transactions (CAS apply
|
targets, the ls-refs response data) and ref transactions (CAS apply
|
||||||
for receive-pack, name validation per git ref rules + reserved-
|
for receive-pack, name validation per git ref rules + reserved-
|
||||||
@@ -123,12 +126,16 @@ planned: call ops only):
|
|||||||
yields the duplex git session.
|
yields the duplex git session.
|
||||||
- **Call ops** (`git/repo/{create,delete,update,get}`) — the JSON half:
|
- **Call ops** (`git/repo/{create,delete,update,get}`) — the JSON half:
|
||||||
thin `OperationSpec`+handler pairs over `Arc<dyn GitRegistryStore>`,
|
thin `OperationSpec`+handler pairs over `Arc<dyn GitRegistryStore>`,
|
||||||
`Visibility::External`, always-on (no gitoxide), gated by
|
`Visibility::External`, always-on (no gitoxide). `create` is gated by
|
||||||
`required_scopes` (global tier) + ownership (self-owned tier, alkcall
|
the static registry check (`required_scopes: ["git:repo:create"]`);
|
||||||
ADR-011 mint-and-own at create). Registered by the assembler wherever
|
`delete`/`update`/`get` carry an empty `AccessControl` and the handler
|
||||||
it wants them exposed. Managing records (op ACL) and git access (repo
|
evaluates the ADR-015 gate — scope `git:admin` OR the record's
|
||||||
grants) are different capabilities — ownership never implies git
|
`manage` grant, via the `authorize` policy function, generic FORBIDDEN
|
||||||
access (ADR-011, ADR-012 §3).
|
denial, unknown-repo ≡ unauthorized (ADR-008). Registered by the
|
||||||
|
assembler wherever it wants them exposed. Managing records (op scopes)
|
||||||
|
and git access (repo grants) are distinct sources — but the record is
|
||||||
|
the single per-repo authz surface, and repo creation seeds the
|
||||||
|
creator's `{read, write, manage}` grants (ADR-015).
|
||||||
|
|
||||||
## Design Decisions
|
## Design Decisions
|
||||||
|
|
||||||
@@ -139,8 +146,9 @@ planned: call ops only):
|
|||||||
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry ids |
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry ids |
|
||||||
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits flow into gen/ingest |
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits flow into gen/ingest |
|
||||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | traits in-crate, impls behind features |
|
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | traits in-crate, impls behind features |
|
||||||
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in records, policy in core |
|
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in records, policy in core (amended: ADR-015) |
|
||||||
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | write trait, file default, CRUD ops, feature split |
|
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | write trait, file default, CRUD ops, feature split (gate amended: ADR-015) |
|
||||||
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-scope-OR-manage gate, create seeds manage |
|
||||||
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | thin-pack ingestion, transaction CAS, report framing |
|
| [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` |
|
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` |
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# ADR-011: Per-repo authorization — grants live in repo records
|
# ADR-011: Per-repo authorization — grants live in repo records
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
Accepted (resolves OQ-08)
|
Accepted (resolves OQ-08; grant action set + policy domain amended by
|
||||||
|
ADR-015)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -47,11 +48,16 @@ metadata must hold.
|
|||||||
|
|
||||||
2. **Grants live in repo records, keyed on the logical identity id.**
|
2. **Grants live in repo records, keyed on the logical identity id.**
|
||||||
`RepoRecord` carries `visibility` (Public/Private) and
|
`RepoRecord` carries `visibility` (Public/Private) and
|
||||||
`grants: identity_id → {read, write}`. Per-repo grants are repo
|
`grants: identity_id → {read, write}` *(amended by ADR-015: the
|
||||||
lifecycle data — created with the repo, administered with it — so
|
action set gains `manage` — `grants: identity_id → {read, write,
|
||||||
they are stored with the record, not folded into `Identity.resources`
|
manage}`, a flat set with no hierarchy; `manage ⊇ write ⊇ read`)*.
|
||||||
(which is static identity-provider data resolved per credential path;
|
Per-repo grants are repo lifecycle data — created with the repo,
|
||||||
per-repo entries would bend that model).
|
administered with it — so they are stored with the record, not folded
|
||||||
|
into `Identity.resources` (which is static identity-provider data
|
||||||
|
resolved per credential path; per-repo entries would bend that
|
||||||
|
model). *(Also amended by ADR-015: the grant-key id format is
|
||||||
|
deliberately an opaque string — the identity namespace is the
|
||||||
|
embedder's to define.)*
|
||||||
|
|
||||||
3. **Authorization is a policy function in alkgit core**, evaluated
|
3. **Authorization is a policy function in alkgit core**, evaluated
|
||||||
against the record:
|
against the record:
|
||||||
@@ -60,6 +66,12 @@ metadata must hold.
|
|||||||
authorize(record, identity: Option<&Identity>, action: read|write) -> bool
|
authorize(record, identity: Option<&Identity>, action: read|write) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*(Amended by ADR-015: the action domain extends to
|
||||||
|
`read|write|manage`; `manage` requires identity plus a `manage`
|
||||||
|
grant, and the grant check is monotonic — a `manage` grant satisfies
|
||||||
|
`write` and `read` checks. The repo-op gate "admin scope OR manage
|
||||||
|
grant" is defined there.)*
|
||||||
|
|
||||||
- `read` on a Public repo → allowed for anyone, including `None`
|
- `read` on a Public repo → allowed for anyone, including `None`
|
||||||
(anonymous-first-class; ADR-007's operative clause).
|
(anonymous-first-class; ADR-007's operative clause).
|
||||||
- `read` on a Private repo → identity required with a read grant.
|
- `read` on a Private repo → identity required with a read grant.
|
||||||
@@ -104,20 +116,25 @@ metadata must hold.
|
|||||||
permission systems is `resolve`-time mapping (no hook, no trait
|
permission systems is `resolve`-time mapping (no hook, no trait
|
||||||
growth); no vault placement question remains for v1.
|
growth); no vault placement question remains for v1.
|
||||||
- **Negative:** two authorization data sources exist by design — op ACL
|
- **Negative:** two authorization data sources exist by design — op ACL
|
||||||
(scopes + ownership, for *managing records*) and repo grants (for *git
|
(scopes, for platform-level capability) and repo grants (for per-repo
|
||||||
access*). Ownership never implies git access: repo creation seeds the
|
capability). This split is deliberate but is a thing a reader must
|
||||||
creator's grants explicitly (ADR-012). This split is deliberate
|
hold both of. *(Amended by ADR-015: repo administration is the
|
||||||
(managing vs accessing are different capabilities) but is a thing a
|
`manage` grant — the record is the single per-repo authz surface;
|
||||||
reader must hold both of.
|
"ownership never implies git access" is superseded — creation seeds
|
||||||
|
`manage`, which is a grant, not an ownership-derived bypass, and
|
||||||
|
grants are promotable via `update_repo`.)*
|
||||||
- **Neutral:** identity-id stability (key rotation) is inherited from
|
- **Neutral:** identity-id stability (key rotation) is inherited from
|
||||||
alkcall's `PeerEntry` model, not guaranteed here — alkgit's contract
|
alkcall's `PeerEntry` model, not guaranteed here — alkgit's contract
|
||||||
is only "grants key on the stable logical id."
|
is only "grants key on the stable logical id."
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- OQ-08 (resolved by this ADR)
|
- OQ-08 (resolved by this ADR), OQ-16 (the grant-key identity-namespace
|
||||||
|
question this ADR's opaque-string rule defers)
|
||||||
- ADR-007 (enforcement point, anonymous-public read rule), ADR-008
|
- ADR-007 (enforcement point, anonymous-public read rule), ADR-008
|
||||||
(registry-resolved ids), ADR-010 (trait family, no-doors rule)
|
(registry-resolved ids), ADR-010 (trait family, no-doors rule)
|
||||||
|
- ADR-015 (the `manage` grant tier — amends this ADR's action set and
|
||||||
|
policy domain)
|
||||||
- alkcall ADR-003/025 (IdentityProvider, `PeerEntry` stable logical id),
|
- alkcall ADR-003/025 (IdentityProvider, `PeerEntry` stable logical id),
|
||||||
ADR-017 (the static ACL engine this complements)
|
ADR-017 (the static ACL engine this complements)
|
||||||
- alktty `tty:open` posture (the scope-constant pattern this ADR
|
- alktty `tty:open` posture (the scope-constant pattern this ADR
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# ADR-012: Registry backing, write surface, and the two-op-kind shape
|
# ADR-012: Registry backing, write surface, and the two-op-kind shape
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
Accepted (resolves OQ-06, OQ-07; amends ADR-010's feature story)
|
Accepted (resolves OQ-06, OQ-07; amends ADR-010's feature story; §3 gate
|
||||||
|
table replaced by ADR-015)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -83,16 +84,20 @@ pub trait GitRegistryStore: GitRegistry {
|
|||||||
separate, additive — built when a deployment with dynamic repo
|
separate, additive — built when a deployment with dynamic repo
|
||||||
management at persistence scale exists.
|
management at persistence scale exists.
|
||||||
|
|
||||||
### 3. CRUD ops shipped in-crate, always-on, External with scope+ownership ACL
|
### 3. CRUD ops shipped in-crate, always-on, External with scope+grant ACL
|
||||||
|
|
||||||
|
*(Gate table amended by ADR-015 — the original "scope OR ownership"
|
||||||
|
formulation was not expressible in alkcall's `AccessControl` (review 001
|
||||||
|
A-1); the two-tier rule below is the ADR-015 shape.)*
|
||||||
|
|
||||||
Ops (names indicative, schema-stable once published):
|
Ops (names indicative, schema-stable once published):
|
||||||
|
|
||||||
| Op | Acts on | Gate |
|
| Op | Acts on | Gate |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `git/repo/create` | store: mint id, allocate root, seed record | scope `git:repo:create` |
|
| `git/repo/create` | store: mint id, allocate root, seed record | scope `git:repo:create` (static registry gate) |
|
||||||
| `git/repo/delete` | store: remove record | scope `git:admin` **or** ownership |
|
| `git/repo/delete` | store: remove record | scope `git:admin` **or** `manage` grant (ADR-015) |
|
||||||
| `git/repo/update` | store: visibility/grants/root | scope `git:admin` **or** ownership |
|
| `git/repo/update` | store: visibility/grants/root | scope `git:admin` **or** `manage` grant (ADR-015) |
|
||||||
| `git/repo/get` (list/read) | registry reads | scope `git:admin` **or** ownership |
|
| `git/repo/get` (list/read) | registry reads | scope `git:admin` **or** `manage` grant (ADR-015) |
|
||||||
|
|
||||||
- **Shape:** ordinary alkcall call ops (`OperationSpec`, JSON,
|
- **Shape:** ordinary alkcall call ops (`OperationSpec`, JSON,
|
||||||
schema-validated), `Visibility::External`, registered by the
|
schema-validated), `Visibility::External`, registered by the
|
||||||
@@ -103,16 +108,21 @@ Ops (names indicative, schema-stable once published):
|
|||||||
authorized-surface), not by hiding the ops. OQ-07's old
|
authorized-surface), not by hiding the ops. OQ-07's old
|
||||||
"Internal ops over an admin listener" framing had the right mechanism
|
"Internal ops over an admin listener" framing had the right mechanism
|
||||||
and the wrong axis.
|
and the wrong axis.
|
||||||
- **Two-tier authorization** uses the checks `AccessControl::check`
|
- **Two-tier authorization** *(superseded by ADR-015's shape: `create`
|
||||||
already composes: `required_scopes` for the global tier,
|
keeps a pure-scope `AccessControl`; `delete`/`update`/`get` carry an
|
||||||
`resource_type` + ownership for the self-owned tier (alkcall ADR-011
|
empty `AccessControl` and the handler evaluates "admin scope OR
|
||||||
— repo create mints ownership via `OwnershipStore::record`, so
|
`authorize(record, identity, manage)`" as one tested function in
|
||||||
"creator administers their own repos" is the ownership path, not a
|
alkgit core, with the generic FORBIDDEN denial and the unknown-repo ≡
|
||||||
grant table).
|
unauthorized rule (ADR-008) at the op layer. Repo create mints
|
||||||
- **Ownership never implies git access.** Repo create seeds the
|
ownership via `OwnershipStore::record` *and seeds the creator's
|
||||||
creator's read/write grants in the record explicitly (ADR-011's
|
`{read, write, manage}` grants* — "creator administers their own
|
||||||
two-data-sources rule: op ACL governs *managing records*, repo grants
|
repos" is now the `manage` grant path.)*
|
||||||
govern *git access*).
|
- **Grants govern access; scopes govern platform capability.** Repo
|
||||||
|
create seeds the creator's `{read, write, manage}` grants in the
|
||||||
|
record explicitly (ADR-015: administration is the `manage` grant —
|
||||||
|
the record is the single per-repo authz surface; op scopes and record
|
||||||
|
grants remain distinct *sources*, platform capability vs per-repo
|
||||||
|
capability).
|
||||||
|
|
||||||
### 4. Feature split (amends ADR-010's single-`gix` feature story)
|
### 4. Feature split (amends ADR-010's single-`gix` feature story)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
# ADR-015: The `manage` grant tier and the repo-op gate
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted (resolves review 001 A-1; amends ADR-011's action set and
|
||||||
|
ADR-012 §3's gate table)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Review 001 (A-1, critical) established that ADR-012 §3's two-tier op gate
|
||||||
|
— "scope `git:admin` **or** ownership" — is not expressible in alkcall's
|
||||||
|
`AccessControl`, which composes restrictions as AND
|
||||||
|
(`required_scopes` fail hard before the ownership block ever runs;
|
||||||
|
spec.rs:94-171). The review's recommended resolution was a handler-side
|
||||||
|
check: admin scope OR `OwnershipProvider::owns(...)`.
|
||||||
|
|
||||||
|
That resolution is correct mechanically but wrong in granularity. "Owner
|
||||||
|
or admin" is the ceiling of what ownership can express, and it is the
|
||||||
|
wrong ceiling: the moment a second maintainer, a bot account, or a
|
||||||
|
collaborator needs to administer a repo they did not create, the model
|
||||||
|
has no answer short of handing out global `git:admin` (too broad) or
|
||||||
|
minting alkcall ownership records for them (wrong home — ownership is
|
||||||
|
spawn-tracking, alkcall ADR-011's mint-and-own records *who created a
|
||||||
|
resource*, not *who may administer it*). The coming platform app
|
||||||
|
(issues, PRs, per-repo admins, teams) needs per-repo administration to
|
||||||
|
be grantable, and the eventual distributed/replicator deployment needs
|
||||||
|
the same grant data to be replicable as flat state.
|
||||||
|
|
||||||
|
The mechanism to express this already exists in the corpus: ADR-011's
|
||||||
|
`authorize` policy function evaluated on the `RepoRecord` — which today
|
||||||
|
knows only `read|write`. The gap is not a missing mechanism; it is a
|
||||||
|
missing action.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. **The grant action set gains `manage`.** ADR-011's
|
||||||
|
`grants: identity_id → {read, write}` becomes
|
||||||
|
`grants: identity_id → {read, write, manage}` — a flat set, no
|
||||||
|
hierarchy: a grant entry is the set of actions one identity holds on
|
||||||
|
one repo, and `manage` includes read and write (a manager of the repo
|
||||||
|
can access it; `authorize` treats `manage ⊇ write ⊇ read` for the
|
||||||
|
actions below it).
|
||||||
|
|
||||||
|
2. **`authorize` is the single per-repo policy function for both git
|
||||||
|
access and repo administration.** Its action domain extends:
|
||||||
|
`authorize(record, identity: Option<&Identity>, action: read|write|manage) -> bool`.
|
||||||
|
Semantics per action:
|
||||||
|
- `read` — unchanged (ADR-011 §3: anonymous-first-class on Public).
|
||||||
|
- `write` — unchanged (identity + write-or-manage grant).
|
||||||
|
- `manage` — identity required, plus a `manage` grant. No anonymous
|
||||||
|
and no owner-bypass path: the owner administers because repo
|
||||||
|
creation *seeds their `manage` grant* (§3), not because identity
|
||||||
|
has any special shape.
|
||||||
|
- Grant-monotonicity: a `manage` grant satisfies `manage`, `write`,
|
||||||
|
and `read` checks; a `write` grant satisfies `write` and `read`;
|
||||||
|
`read` satisfies `read` only.
|
||||||
|
|
||||||
|
3. **The repo-op gate is: global admin scope OR `manage` grant.**
|
||||||
|
ADR-012 §3's gate table becomes:
|
||||||
|
|
||||||
|
| Op | Gate |
|
||||||
|
|---|---|
|
||||||
|
| `git/repo/create` | scope `git:repo:create` (static registry gate, unchanged) |
|
||||||
|
| `git/repo/delete` | scope `git:admin` **or** `manage` grant (handler-evaluated) |
|
||||||
|
| `git/repo/update` | scope `git:admin` **or** `manage` grant (handler-evaluated) |
|
||||||
|
| `git/repo/get` (list/read) | scope `git:admin` **or** `manage` grant (handler-evaluated) |
|
||||||
|
|
||||||
|
Mechanically this is review 001's option (a) with the OR-term
|
||||||
|
generalized from ownership to the `manage` grant: `create` keeps its
|
||||||
|
pure-scope `AccessControl` (`required_scopes: ["git:repo:create"]` —
|
||||||
|
the static engine serves it, AND-composition is fine with one
|
||||||
|
restriction); `delete`/`update`/`get` carry an empty
|
||||||
|
`AccessControl::default()` and the handler evaluates the two-tier
|
||||||
|
rule as one tested function in alkgit core (admin scope in
|
||||||
|
`Identity.scopes` OR `authorize(record, identity, manage)`), denying
|
||||||
|
with the generic FORBIDDEN. The ADR-008 rule is preserved at the op
|
||||||
|
layer: unknown-repo and not-authorized collapse to the same denial —
|
||||||
|
no existence oracle.
|
||||||
|
|
||||||
|
4. **Repo creation seeds the creator's `manage` grant.** `git/repo/create`
|
||||||
|
mints alkcall ownership (alkcall ADR-011, `OwnershipStore::record` —
|
||||||
|
unchanged; ownership remains the platform's spawn-tracking record)
|
||||||
|
**and** writes the creator's grants as the full set `{read, write,
|
||||||
|
manage}` in the `RepoRecord`. "Creator administers their own repos"
|
||||||
|
becomes a grant, administered with the repo: the creator can
|
||||||
|
`update_repo` to promote a collaborator to `manage` or demote
|
||||||
|
themselves, using the op that already exists (`update_repo`), with no
|
||||||
|
new op and no trait change.
|
||||||
|
|
||||||
|
5. **alkcall's `AccessControl` is not extended.** The scope-OR-grant
|
||||||
|
composition lives handler-side, which is exactly where ADR-011 §4
|
||||||
|
put policy ("data and policy are split across the backend seam" —
|
||||||
|
data in the backend, policy in core). This sidesteps
|
||||||
|
`AccessControl`'s AND-composition rather than changing it: an
|
||||||
|
upstream family-crate change (review 001's option (c)) is not
|
||||||
|
required, and the two-tier rule stays unit-testable in-crate instead
|
||||||
|
of hiding in an assembly-supplied `OwnershipProvider` overload
|
||||||
|
(option (b)'s rejected shape).
|
||||||
|
|
||||||
|
6. **Ownership no longer implies administration; grants do.** This
|
||||||
|
supersedes the ADR-011/ADR-012 sentence "ownership never implies git
|
||||||
|
access" — the two-data-sources rule is *retired*, not extended: repo
|
||||||
|
records now carry the grant state both access tiers evaluate. The
|
||||||
|
op ACL (scopes) and the record grants remain distinct *sources* —
|
||||||
|
scopes govern platform-level capability, grants govern per-repo
|
||||||
|
capability — but the record is now the single per-repo surface. The
|
||||||
|
`manage` grant is the app's promotion path today (the platform app
|
||||||
|
compiles roles/teams into flat grants and writes them with
|
||||||
|
`update_repo`); nothing about roles, teams, or orgs enters the
|
||||||
|
protocol crate — ADR-012 §5's split trigger stands: that is
|
||||||
|
downstream's crate, compiling down to these flat grants.
|
||||||
|
|
||||||
|
7. **Grant keys are opaque strings; the id namespace is deliberately
|
||||||
|
open.** Grants key on the resolved identity's stable logical id
|
||||||
|
(ADR-011 §1), which is alkcall-cluster-local today. The id format is
|
||||||
|
deliberately unspecified at the alkgit layer — an opaque string — so
|
||||||
|
a downstream identity space (e.g. a `user/repo`-shaped, or
|
||||||
|
contract-derived, globally-comparable id) can be introduced by the
|
||||||
|
assembly/identity-provider seam without an alkgit change. Two
|
||||||
|
identities colliding on one string is the embedder's identity
|
||||||
|
guarantee to make, not alkgit's.
|
||||||
|
|
||||||
|
8. **Flat grants are the replication substrate.** Grants being flat
|
||||||
|
sets keyed on opaque id strings, carried in the record, and mutated
|
||||||
|
only through the store's `update_repo` is precisely the shape a
|
||||||
|
future replicator/hub needs: an external authority (the planned
|
||||||
|
contract watcher) can hold and publish grant state, and the local
|
||||||
|
store consumes it as records. Nothing here builds replication — but
|
||||||
|
the data model is chosen so that the external-authority case is
|
||||||
|
`update_repo` calls from a sync writer, not a schema change.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Positive:** A-1 resolves without upstream changes; per-repo
|
||||||
|
administration becomes grantable (the collaborator/admin case is now
|
||||||
|
expressible); one policy function serves git access and repo
|
||||||
|
management; the eventual app's roles/teams compile to flat grants
|
||||||
|
with no protocol-crate growth; the record is the single per-repo authz
|
||||||
|
surface a replicator would mirror.
|
||||||
|
- **Negative:** the grant set widens from two to three actions — a
|
||||||
|
wire-schema decision that enters OQ-03's freeze inventory (N-3's
|
||||||
|
schema pinning must land *after* this, so the freeze captures the
|
||||||
|
three-action shape once); the two-data-sources consequence recorded in
|
||||||
|
ADR-011 is partially retired, so readers of that ADR must follow the
|
||||||
|
amendment (recorded there).
|
||||||
|
- **Neutral:** ownership records and grants coexist with distinct
|
||||||
|
purposes (platform spawn-tracking vs per-repo capability); nothing in
|
||||||
|
v1 reads ownership for authorization except the create-op's mint, so
|
||||||
|
the mint could even become optional in a downstream that doesn't want
|
||||||
|
spawn-tracking — kept in v1 because `git/repo/*` ops are family-shared
|
||||||
|
and the record is cheap.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Review 001 A-1 (the trigger; this ADR takes its option (a) shape with
|
||||||
|
the OR-term generalized), N-3 (the schema-pinning rider this ADR must
|
||||||
|
precede)
|
||||||
|
- ADR-011 (the `authorize` policy function and grant storage this ADR
|
||||||
|
amends), ADR-012 §3 (the gate table this ADR replaces), ADR-012 §5
|
||||||
|
(the platform-crate split trigger this ADR preserves)
|
||||||
|
- ADR-007 (enforcement order), ADR-008 (unknown ≡ unauthorized at the
|
||||||
|
op layer), ADR-010 (trait family, no-doors)
|
||||||
|
- alkcall ADR-011 (ownership mint-and-own — retained as spawn-tracking),
|
||||||
|
`AccessControl` (spec.rs:94 — the AND-composition this ADR routes
|
||||||
|
around, not through)
|
||||||
|
- vision.md §"Primary deployment target" (the grant set remains
|
||||||
|
simple enough to reason about completely)
|
||||||
|
- backend.md §"The two op kinds" (op-gate restatement)
|
||||||
@@ -26,9 +26,10 @@ the door's key-based identity) resolving an alkcall `Identity` (the
|
|||||||
identity-extractor seam that ADR-006 needed exists only in the door, where
|
identity-extractor seam that ADR-006 needed exists only in the door, where
|
||||||
it belongs). alkgit consumes the resolved identity and the registry
|
it belongs). alkgit consumes the resolved identity and the registry
|
||||||
record: the per-repo check is alkgit-core's `authorize` policy function
|
record: the per-repo check is alkgit-core's `authorize` policy function
|
||||||
(ADR-011 — public+read anonymous-first-class, write always
|
(ADR-011, ADR-015 — public+read anonymous-first-class, write always
|
||||||
authenticated+granted), run at ADR-007's step-3 position by every door
|
authenticated+granted, `manage` for repo administration), run at
|
||||||
and by the channels open-op gate. There is no door-specific auth surface
|
ADR-007's step-3 position by every door and by the channels open-op
|
||||||
|
gate. There is no door-specific auth surface
|
||||||
in alkgit and no scope constant — per-repo grants replaced the
|
in alkgit and no scope constant — per-repo grants replaced the
|
||||||
`tty:open`-style gate (the single-scope shape cannot express
|
`tty:open`-style gate (the single-scope shape cannot express
|
||||||
anonymous-public fetch).
|
anonymous-public fetch).
|
||||||
@@ -111,16 +112,18 @@ deployment's docs, not here.
|
|||||||
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry ids |
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry ids |
|
||||||
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits on every session |
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | limits on every session |
|
||||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | doors are family infrastructure |
|
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | doors are family infrastructure |
|
||||||
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | `authorize` policy, grants in records |
|
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | `authorize` policy, grants in records (amended: ADR-015) |
|
||||||
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | op registration surface, two op kinds |
|
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | op registration surface, two op kinds (gate amended: ADR-015) |
|
||||||
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push advertisement per door, report framing |
|
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push advertisement per door, report framing |
|
||||||
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | one round per POST; ack section is per-round, stateless |
|
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | one round per POST; ack section is per-round, stateless |
|
||||||
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage op gate |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
- None. (OQ-08 resolved by ADR-011 — door auth mechanics stay here,
|
- None. (OQ-08 resolved by ADR-011 — door auth mechanics stay here,
|
||||||
registry identity model settled; ADR-012 §3 pins the op registration
|
registry identity model settled; ADR-012 §3 pins the op registration
|
||||||
surface.)
|
surface, its gate amended by ADR-015. OQ-16 — grant-key identity
|
||||||
|
namespace — lives at the identity-provider seam, not the door.)
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ when their impacts say so; it records how careful the resolution must be.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| OQ-05 | deferred(scope) | ecosystem need for sha256 |
|
| OQ-05 | deferred(scope) | ecosystem need for sha256 |
|
||||||
| OQ-03 | partially resolved | first-publish timing (release decision; the freeze inventory lives in backend.md/transport.md) |
|
| OQ-03 | partially resolved | first-publish timing (release decision; the freeze inventory lives in backend.md/transport.md) |
|
||||||
|
| OQ-16 | deferred(scope) | first cross-assembly record sharing (distributed-git phase; tracker `architecture/oq-16-grant-identity-namespace`) |
|
||||||
|
|
||||||
OQ-02 and OQ-04 resolved this cycle (ADR-014, ADR-013); OQ-08/06/07/09/01
|
OQ-02 and OQ-04 resolved this cycle (ADR-014, ADR-013); OQ-08/06/07/09/01
|
||||||
resolved in earlier cycles. No `open` or `deferred(unclear)` questions
|
resolved in earlier cycles. OQ-16 is deferred but blocks nothing in v1
|
||||||
remain.
|
(grants key on cluster-local ids within one assembly).
|
||||||
|
|
||||||
## Theme: composition / crate shapes
|
## Theme: composition / crate shapes
|
||||||
|
|
||||||
@@ -152,10 +153,48 @@ remain.
|
|||||||
function (the clause ADR-007 pinned but the static ACL engine could
|
function (the clause ADR-007 pinned but the static ACL engine could
|
||||||
not express); vault placement resolved as **nothing to place in v1**
|
not express); vault placement resolved as **nothing to place in v1**
|
||||||
(no credential-shaped material in metadata).
|
(no credential-shaped material in metadata).
|
||||||
|
*(Grant-key id format declared deliberately open by ADR-015 §7 — the
|
||||||
|
cross-identity-namespace question this leaves is tracked as OQ-16.)*
|
||||||
- **Resolution**: [decisions/011-per-repo-authorization.md]
|
- **Resolution**: [decisions/011-per-repo-authorization.md]
|
||||||
- **Cross-references**: ADR-007, ADR-008, ADR-011, ADR-012, OQ-06, OQ-07,
|
- **Cross-references**: ADR-007, ADR-008, ADR-011, ADR-012, OQ-06, OQ-07,
|
||||||
backend.md §GitRegistry, doors.md
|
backend.md §GitRegistry, doors.md
|
||||||
|
|
||||||
|
### OQ-16: Grant-key identity namespace (globally-comparable ids for multi-hub grants)
|
||||||
|
|
||||||
|
- **Origin**: ADR-015 §7; user session (2026-09-26)
|
||||||
|
- **Status**: deferred(scope)
|
||||||
|
- **Door type**: two-way (schema field format; grants are already opaque
|
||||||
|
strings, so this constrains only the *contents*, not the alkgit API)
|
||||||
|
- **Priority**: low (blocks nothing in v1 — grants key on alkcall's
|
||||||
|
cluster-local id within one assembly, which is the only deployment
|
||||||
|
v1 serves)
|
||||||
|
- **Question**: what identity-id format do grants key on when repo
|
||||||
|
records are shared across assemblies (the distributed/replicator
|
||||||
|
deployment: contract-watching hubs mirroring grant state)? Today the
|
||||||
|
id is alkcall's `Identity.id` — stable within one cluster, but
|
||||||
|
meaningless across hubs. Candidate shapes: `{namespace}/{id}`
|
||||||
|
compound ids (the `{user|org}/{repo}` convention familiar from
|
||||||
|
self-hosted git — e.g. `alkdev/alkgit` locally, `alkimiadev/alkgit`
|
||||||
|
when mirrored — extended to identity: `gh:alkimiadev`,
|
||||||
|
`alk:alkdev`), did-key / contract-derived identifiers (the planned
|
||||||
|
smart-contract org/user/repo model), or a two-part
|
||||||
|
(authority, local-id) tuple.
|
||||||
|
- **Constraints any resolution must satisfy** (recorded from ADR-015 §7):
|
||||||
|
grants remain flat sets keyed on opaque strings; alkgit never parses,
|
||||||
|
validates, or interprets the id — collision-freedom is the
|
||||||
|
identity-provider's guarantee; a downstream namespace can be
|
||||||
|
introduced at the assembly seam without an alkgit change.
|
||||||
|
- **Impacts**: only the downstream identity-provider seam and the
|
||||||
|
future platform app / replicator grant-sync format. The v1
|
||||||
|
`authorize` policy function, the grant schema shape, and the op gate
|
||||||
|
are all id-format-agnostic by construction.
|
||||||
|
- **Blocked on**: the first deployment that shares repo records across
|
||||||
|
assemblies (the distributed-git phase; also touches alkcall's
|
||||||
|
identity model if a family-wide id format is preferred over a
|
||||||
|
payload-level namespace convention). Tracker task:
|
||||||
|
`tasks/architecture/oq-16-grant-identity-namespace.md`.
|
||||||
|
- **Cross-references**: ADR-011 §1, ADR-015 §7, OQ-08
|
||||||
|
|
||||||
## Theme: storage / metadata
|
## Theme: storage / metadata
|
||||||
|
|
||||||
### OQ-06: Registry/metadata backing store (gix feature)
|
### OQ-06: Registry/metadata backing store (gix feature)
|
||||||
@@ -179,14 +218,16 @@ remain.
|
|||||||
- **Status**: **resolved** — ADR-012 (§3, §5): the crate ships a minimal
|
- **Status**: **resolved** — ADR-012 (§3, §5): the crate ships a minimal
|
||||||
CRUD op set (`git/repo/{create,delete,update,get}`) as thin call ops
|
CRUD op set (`git/repo/{create,delete,update,get}`) as thin call ops
|
||||||
over `GitRegistryStore`, `Visibility::External`, always-on, gated by
|
over `GitRegistryStore`, `Visibility::External`, always-on, gated by
|
||||||
scopes (global tier, e.g. `git:admin` / `git:repo:create`) + ownership
|
the global admin scope and per-repo grants. The old "Internal ops over
|
||||||
(self-owned tier; create mints ownership per alkcall ADR-011 and
|
an admin listener" framing is superseded (right mechanism, wrong axis
|
||||||
seeds the creator's grants — ownership never implies git access,
|
— visible-surface = authorized-surface is served by the ACL). The
|
||||||
ADR-011). The old "Internal ops over an admin listener" framing is
|
gate table as written used scope+ownership and is replaced by ADR-015
|
||||||
superseded (right mechanism, wrong axis — visible-surface =
|
(the `manage` grant tier — admin scope OR manage grant, handler-
|
||||||
authorized-surface is served by the ACL). Ops stay record-scoped;
|
evaluated; create seeds the creator's `{read, write, manage}` grants).
|
||||||
anything beyond repo records (users, orgs) is a downstream platform
|
Ops stay record-scoped; anything beyond repo records (users, orgs) is
|
||||||
crate (the recorded split trigger, ADR-012 §5).
|
a downstream platform crate (the recorded split trigger, ADR-012 §5) —
|
||||||
|
the app compiles roles/teams into flat grants (ADR-015 §6).
|
||||||
- **Resolution**: [decisions/012-registry-backing-and-ops.md] §3, §5
|
- **Resolution**: [decisions/012-registry-backing-and-ops.md] §3, §5
|
||||||
- **Cross-references**: ADR-007, ADR-011, ADR-012, alkcall ADR-017/011,
|
(gate amended by [decisions/015-manage-grant-and-op-gate.md])
|
||||||
backend.md §"The two op kinds"
|
- **Cross-references**: ADR-007, ADR-011, ADR-012, ADR-015, alkcall
|
||||||
|
ADR-017/011, backend.md §"The two op kinds"
|
||||||
@@ -88,22 +88,28 @@ multi-round negotiation are design-complete against real-client captures
|
|||||||
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs |
|
| [008](decisions/008-registry-resolved-repo-identity.md) | Repo identity | wire names are registry IDs |
|
||||||
| [009](decisions/009-bounded-resources-budget.md) | Budgets | every session carries limits |
|
| [009](decisions/009-bounded-resources-budget.md) | Budgets | every session carries limits |
|
||||||
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | single crate, producer/consumer halves, no doors/binary |
|
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | single crate, producer/consumer halves, no doors/binary |
|
||||||
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in repo records, policy in core, vault-nil |
|
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in repo records, policy in core, vault-nil (amended: ADR-015) |
|
||||||
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | read/write split, file default, CRUD ops, feature split |
|
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | read/write split, file default, CRUD ops, feature split |
|
||||||
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push machine, thin-pack acceptance, unpack-first CAS |
|
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | V0-framed push machine, thin-pack acceptance, unpack-first CAS |
|
||||||
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` |
|
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` |
|
||||||
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage gate, create seeds manage |
|
||||||
|
| [015](decisions/015-manage-grant-and-op-gate.md) | Manage grant + op gate | `manage` tier, admin-OR-manage gate, create seeds manage |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
Key questions tracked in [open-questions.md](open-questions.md):
|
Key questions tracked in [open-questions.md](open-questions.md):
|
||||||
|
|
||||||
- **OQ-03**: publish-time API freeze inventory (the `git/repo/*` op set
|
- **OQ-03**: publish-time API freeze inventory (the `git/repo/*` op set
|
||||||
and the trait family enter it; ADR-012, ADR-013/014's trait additions).
|
and the trait family enter it; ADR-012, ADR-013/014's trait additions;
|
||||||
|
the ADR-015 three-action grant shape must land in it).
|
||||||
- **OQ-05**: sha256 policy (deferred(scope), low).
|
- **OQ-05**: sha256 policy (deferred(scope), low).
|
||||||
|
- **OQ-16**: grant-key identity namespace (deferred(scope); blocks
|
||||||
|
nothing in v1 — ADR-015 §7).
|
||||||
|
|
||||||
Resolved this cycle: OQ-04 (ADR-013 — receive-pack state machine,
|
Resolved this cycle: OQ-04 (ADR-013 — receive-pack state machine,
|
||||||
capture-grounded), OQ-02 (ADR-014 — negotiation ack loop). Earlier:
|
capture-grounded), OQ-02 (ADR-014 — negotiation ack loop). Earlier:
|
||||||
OQ-08 (ADR-011 — per-repo authorization, grants in records, vault-nil),
|
OQ-08 (ADR-011 — per-repo authorization, grants in records, vault-nil),
|
||||||
OQ-06 (ADR-012 — `registry-file` default, persistence adapters
|
OQ-06 (ADR-012 — `registry-file` default, persistence adapters
|
||||||
additive), OQ-07 (ADR-012 — CRUD ops shipped External with
|
additive), OQ-07 (ADR-012 — CRUD ops shipped External with scope+grant
|
||||||
scope+ownership ACL), OQ-09/OQ-01 (ADR-010 — pure protocol crate).
|
ACL, gate amended by ADR-015), OQ-09/OQ-01 (ADR-010 — pure protocol
|
||||||
|
crate).
|
||||||
@@ -123,6 +123,9 @@ in use**. So:
|
|||||||
- Push is always authenticated, on every repo, no exceptions.
|
- Push is always authenticated, on every repo, no exceptions.
|
||||||
- ACL must be simple enough to reason about completely: repo visibility
|
- ACL must be simple enough to reason about completely: repo visibility
|
||||||
(public/private) + identity-based read/write, nothing richer in v1.
|
(public/private) + identity-based read/write, nothing richer in v1.
|
||||||
|
*(Amended by ADR-015: the grant set gains `manage` — repo
|
||||||
|
administration — still flat, still per-repo, still one policy
|
||||||
|
function.)*
|
||||||
|
|
||||||
## What phase 0 must still produce before phase 1
|
## What phase 0 must still produce before phase 1
|
||||||
|
|
||||||
@@ -143,6 +146,8 @@ in use**. So:
|
|||||||
- Registry management ops, if the gix feature ships any, are alkcall
|
- Registry management ops, if the gix feature ships any, are alkcall
|
||||||
`Visibility::Internal` ops over an admin-only listener; they are never
|
`Visibility::Internal` ops over an admin-only listener; they are never
|
||||||
part of the git traffic surface (OQ-07).
|
part of the git traffic surface (OQ-07).
|
||||||
|
*(Superseded by ADR-012 §3 and ADR-015: the ops are External,
|
||||||
|
gated by scope + the `manage` grant — right mechanism, wrong axis.)*
|
||||||
- Repo names arriving on the wire are registry IDs, never paths; storage
|
- Repo names arriving on the wire are registry IDs, never paths; storage
|
||||||
roots are configured server-side only.
|
roots are configured server-side only.
|
||||||
- The advertisement phase runs ACL before the first ref line is emitted.
|
- The advertisement phase runs ACL before the first ref line is emitted.
|
||||||
|
|||||||
@@ -758,7 +758,7 @@ criticals are ADR-writing work, not code):
|
|||||||
|
|
||||||
| ID | Finding | Recommended fix | Effort | Risk | Status |
|
| ID | Finding | Recommended fix | Effort | Risk | Status |
|
||||||
|----|---------|----------------|--------|------|--------|
|
|----|---------|----------------|--------|------|--------|
|
||||||
| A-1 | op-gate OR not expressible in `AccessControl` | new ADR (or ADR-012 §3 amendment): handler-side two-tier check, create keeps static scope gate | small | none | open |
|
| A-1 | op-gate OR not expressible in `AccessControl` | new ADR (or ADR-012 §3 amendment): handler-side two-tier check, create keeps static scope gate | small | none | **resolved (ADR-015)** — option (a) shape with the OR-term generalized to the `manage` grant |
|
||||||
| A-2 | `async fn` traits not dyn-compatible | ADR-012 §1 + backend.md amendment: `#[async_trait]`; add `async-trait = "0.1"` to manifest | small | none | open |
|
| A-2 | `async fn` traits not dyn-compatible | ADR-012 §1 + backend.md amendment: `#[async_trait]`; add `async-trait = "0.1"` to manifest | small | none | open |
|
||||||
| A-3 | native preamble / service dimension unpinned | new ADR: open-op params `{repo, service}`, session tuple + stateless entry gain the service selector, `GitAdapter` preamble pinned | moderate | wire-format (freeze inventory) | open |
|
| A-3 | native preamble / service dimension unpinned | new ADR: open-op params `{repo, service}`, session tuple + stateless entry gain the service selector, `GitAdapter` preamble pinned | moderate | wire-format (freeze inventory) | open |
|
||||||
| A-4 | done-round boundary set unverified | ADR-014 §2 + transport.md clause: boundary = `common_haves`-filtered haves | trivial | none | open |
|
| A-4 | done-round boundary set unverified | ADR-014 §2 + transport.md clause: boundary = `common_haves`-filtered haves | trivial | none | open |
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
id: architecture/oq-16-grant-identity-namespace
|
||||||
|
name: OQ-16 unblock — first cross-assembly record sharing
|
||||||
|
status: pending
|
||||||
|
depends_on: []
|
||||||
|
scope: narrow
|
||||||
|
risk: trivial
|
||||||
|
impact: component
|
||||||
|
level: research
|
||||||
|
tags: [external-trigger, deferred-oq]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Tracker for OQ-16 (`deferred(scope)`): the grant-key identity namespace.
|
||||||
|
Grants on `RepoRecord` key on opaque identity-id strings (ADR-011,
|
||||||
|
ADR-015 §7). Within one assembly those are alkcall's cluster-local
|
||||||
|
`Identity.id` — sufficient for v1. The question of a
|
||||||
|
globally-comparable id format (compound `{namespace}/{id}` shapes,
|
||||||
|
did-key/contract-derived identifiers, or (authority, local-id)
|
||||||
|
tuples) only becomes real when repo records are shared across
|
||||||
|
assemblies — the distributed-git/replicator phase, where a hub mirrors
|
||||||
|
grant state published by an external authority.
|
||||||
|
|
||||||
|
Constraints any resolution must satisfy are already recorded in the OQ
|
||||||
|
entry (ADR-015 §7): grants stay flat sets keyed on opaque strings;
|
||||||
|
alkgit never parses or interprets the id; collision-freedom is the
|
||||||
|
identity-provider's guarantee.
|
||||||
|
|
||||||
|
## Work
|
||||||
|
|
||||||
|
None while blocked. When the first cross-assembly record-sharing
|
||||||
|
deployment is designed (the distributed-git phase, or a platform app
|
||||||
|
that federates grant state), decide the id format: either a
|
||||||
|
payload-level namespace convention (alkgit-side, opaque strings with a
|
||||||
|
namespaced grammar) or a family-wide identity-id format (alkcall-level
|
||||||
|
change). Record the resolution in an ADR; update the OQ entry.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- OQ-16 status matches `docs/architecture/open-questions.md`.
|
||||||
|
- If resolved: an ADR exists and the grant-schema freeze inventory
|
||||||
|
(OQ-03) reflects the chosen id format.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Building the distributed-git replication machinery (separate phase).
|
||||||
|
- Changing alkcall's `Identity` model (only if the family-wide option
|
||||||
|
is chosen, and as that crate's own decision).
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
> Filled on completion.
|
||||||
Reference in new issue
Block a user