W1 (call/protocol/abort-cascade-wiring): wire AbortCascade into CallAdapter handle_stream for EVENT_ABORTED. W2 (core/endpoint-client-fingerprint): extract TLS client cert fingerprint in dispatch_quinn/dispatch_iroh. W3 (vault/mnemonic-debug-redaction): replace Mnemonic derive(Debug) with redacting impl. W4 (core/auth-apikey-resources, level: research): decide whether ApiKeyEntry should carry resources, then implement or drop from spec. review-post-impl-fixes gates on all four. Graph: 33 tasks, 12 gens.
5.7 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level |
|---|---|---|---|---|---|---|---|
| core/auth-apikey-resources | Reconcile ApiKeyEntry.resources — add field to type and populate in resolve_api_key, or drop from spec | pending | narrow | low | component | research |
Description
Three-way mismatch between spec, type, and implementation for resource-scoped ACLs on API-key-authenticated identities:
-
Spec (
docs/architecture/crates/core/auth.md:153):"Token: ... return
Identity { id: prefix, scopes: entry.scopes, resources: entry.resources }." The spec referencesentry.resources. -
Type (
crates/alknet-core/src/config.rs:55–62):ApiKeyEntryhas fieldsprefix, hash, scopes, description, expires_at— there is noresourcesfield. Soentry.resourcesin the spec cannot be implemented as written. -
Implementation (
config.rs:113–117):resolve_api_keyconstructs the resolvedIdentitywithresources: std::collections::HashMap::new()— resources are always empty, regardless of what the API key grants.
The same gap exists in resolve_identity_from_fingerprint
(config.rs:69–79), which also returns resources: HashMap::new().
Impact
Latent today: no operation in the workspace uses resource-based ACLs
against a token- or fingerprint-resolved identity. The
AccessControl::resource_type / resource_action fields exist in
OperationSpec (spec.rs:32–37) and are tested (spec.rs:284–303), but
those tests always hand-construct Identity.resources directly —
never via the resolver path. The moment an operation declares a
resource-scoped ACL and a caller authenticates via API key, the ACL
check will fail with "missing resource" even if the key was granted
that resource in config — because resources is always empty.
This is a research/decision task, not an implementation task
The decomposer rule applies: the architecture is ambiguous on whether API keys should grant resource-scoped access. Two valid designs exist; pick one and document it before implementing. Do not implement until the decision is made.
Option A — add resources to ApiKeyEntry (matches current spec):
- Add
pub resources: HashMap<String, Vec<String>>toApiKeyEntry. - Update
resolve_api_keyto populateIdentity.resourcesfromentry.resources. - Update
resolve_identity_from_fingerprintsimilarly — either add aresourcesfield to the fingerprint config path, or document that fingerprint auth grants scopes only (resources empty). - Update
auth.md's token resolution example to match the new field. - Define the TOML schema for
resourcesinAuthPolicy(when a TOML schema is added — currently config is built in code, not parsed). - Resource-scoped ACLs then work for both auth paths.
Option B — drop resources from the spec for API keys:
- Remove
entry.resourcesfromauth.md:153. - Document that API keys grant scopes only; resource-scoped access requires a different identity source (e.g., a future OAuth/JWT provider that carries resource claims).
Identity.resourcesstays in the type (it's used by hand-constructed identities in tests and byCompositionAuthority::as_identityfor internal calls) but token/fingerprint resolvers always return empty.- Resource-scoped ACLs against token identities return
Forbidden— this becomes a documented limitation, not a bug.
Deliverable
Produce a short decision note (a paragraph in auth.md under
"Identity Resolution" — or a new ADR if the decision feels
consequential enough) that picks A or B and justifies it. Then either
implement the chosen option in the same task (if small) or split a
follow-up level: implementation task gated on this one.
The decision should consider: do any planned operations (in the
upcoming alknet-ssh, alknet-fs, alknet-git crates) need resource-scoped
ACLs on API-key identities? If yes, A. If resource ACLs are only ever
applied to handler-internal composition identities
(CompositionAuthority), B is fine and simpler.
Acceptance Criteria
- Decision made: Option A or Option B
- Decision documented in
auth.md(or a new ADR if consequential) - If Option A:
ApiKeyEntry.resourcesadded,resolve_api_keypopulatesIdentity.resources,resolve_identity_from_fingerprinthandling decided and documented,auth.md:153matches the new shape - If Option B:
auth.md:153corrected to dropentry.resources, limitation documented - Either way: a test covering the chosen behavior (token resolves with resources, or token resolves with empty resources + documented limitation)
cargo test -p alknet-coresucceedscargo clippy -p alknet-core --all-targetssucceeds with no warnings
References
- docs/reviews/004-post-implementation-sanity-check.md — W4 (full finding)
- docs/architecture/crates/core/auth.md:152–153 — spec text referencing
entry.resources - crates/alknet-core/src/config.rs:55–62 —
ApiKeyEntry(missingresources) - crates/alknet-core/src/config.rs:69–118 — both resolvers returning empty
resources - crates/alknet-call/src/registry/spec.rs:77–103 —
AccessControl::checkresource path (the consumer that would fail) - crates/alknet-call/src/registry/context.rs:58–65 —
CompositionAuthority::as_identity(the internal-call path that does populateresources)
Notes
This is a
level: researchtask because the fix is small but the decision is not. The decomposer principle: if architecture is ambiguous, do not proceed with implementation — escalate. Make the decision first, then implement. If the decision is A and the implementation is more than ~30 lines, split a follow-uplevel: implementationtask (core/auth-apikey-resources-impl) depending on this one.