Skip to content

RFC: content-addressed authority profiles#36

Draft
joshuajbouw wants to merge 5 commits into
mainfrom
agent/content-addressed-authority-profiles
Draft

RFC: content-addressed authority profiles#36
joshuajbouw wants to merge 5 commits into
mainfrom
agent/content-addressed-authority-profiles

Conversation

@joshuajbouw

@joshuajbouw joshuajbouw commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replaces wildcard administrative authority with content-addressed exact profiles
  • fixes canonical authority/profile identities to domain-separated BLAKE3
  • self-heals persisted wildcard state before authenticated control connections are enabled
  • defines the capability registry, boot migration, repair, recovery, and compatibility requirements

Why

The current wildcard authority can inherit capabilities added in the future and can activate bypasses that are not represented in the capability catalog. Astrid needs explicit, inspectable administrative authority without giving up complete sysadmin control.

Status

Draft. This RFC must remain open and unmerged. Generated registry and profile digests, migration fixtures, and implementation artifacts remain acceptance gates.

Tracks astrid-runtime/astrid#1228. Companion design: #37.

Validation

  • git diff --check
  • python3 generate-book.py

joshuajbouw added a commit to astrid-runtime/astrid that referenced this pull request Jul 15, 2026
## Linked Issue

Closes #1233

Refs #1228 and astrid-runtime/rfcs#36.

## Summary

Adds passive content-addressed capability-registry primitives and moves
short device
key IDs to BLAKE3 before wider adoption. The profile shape remains
unchanged and
stored device IDs self-heal from their public keys; active device-scoped
bearer
sessions re-authenticate after upgrade.

## Changes

- add exact capability IDs with borrowed and owned storage
- add validated 32-byte entry, registry, and signed-extension-package
digest newtypes
- type the nonzero capability-registry schema revision
- add content-bound capability references and exact resolution
- add registered definitions and canonical registry manifests
- implement deterministic canonical CBOR and domain-separated BLAKE3
identities
- derive device key IDs from BLAKE3 with a legacy SHA-256 self-heal
regression
- freeze the private 51-ID capability-registry revision 1 set
- isolate hashing, encoding, checked conversions, and digest validation
in a private util module
- encode canonical integer widths with one checked conversion per
representation
- keep the registry as a public Astrid runtime API without exposing it
over HTTP, WIT,
  or the control socket
- cover canonical bytes, BLAKE3 digest goldens, tampering, duplicates,
invalid lengths,
  ordering, schema revisions, and fail-closed errors

## Verification

- `cargo fmt --all -- --check`
- `cargo metadata --locked --offline --no-deps`
- `git diff --check`
- golden vectors independently recomputed with `b3sum`
- device-key BLAKE3 golden independently recomputed with `b3sum`
- targeted local compilation reached the existing native BLAKE3
build-script stall;
  GitHub CI is the execution gate for the new head

## Checklist

- [x] Linked to an issue
- [x] CHANGELOG.md updated under `[Unreleased]`
joshuajbouw added a commit to astrid-runtime/astrid that referenced this pull request Jul 15, 2026
## Linked Issue

Closes #1237

Refs #1228 and astrid-runtime/rfcs#36.

## Summary

Preserves the authenticating device's attenuation across every kernel
authority view touched by capsule inventory, agent/group listing, and
pair-device delegation. No product-specific trust path or public wire
change is introduced.

## Changes

- centralize canonical device-key resolution with malformed, unknown,
and revoked IDs denied
- pin the authorized principal profile, group config, and resolved
device scope at one policy-decision point per request
- use that exact authorization snapshot for capsule inventory,
agent/group visibility, and pair delegation
- derive the dispatch principal from the snapshot so authority and
identity cannot diverge internally
- preserve existing no-device and `Full` device behavior
- validate every explicit pair-scope allow and deny pattern through the
canonical typed capability boundary before token persistence
- require effective `self:auth:pair:admin` in the common authorization
preamble for `Full`, preset-full, and bare-`*` scopes
- preflight pair-specific subset and structural no-escalation rules
against the pinned authorization snapshot before recording success
- record one failure audit row for missing pair-admin, scoped-to-full,
and universal subset denials without a preceding success row
- classify malformed pair request input as an authorized operation
failure rather than a permission denial or security event
- retain issuer denies in every scoped child
- make profile-cache invalidation generation-aware per principal so an
older concurrent disk load cannot republish pre-revocation authority and
unrelated principals do not retry
- refresh idempotent profile persistence directly from the disk state
already loaded under the cache write lock, avoiding an unnecessary
generation bump and reload while preserving mutation invalidation
- keep unknown-device assertions at the router authorization boundary
rather than private handlers that cannot receive them in production
- preserve one outward scope-denial reason for malformed, unknown,
revoked, and valid scoped denials so authorization responses do not
become a device-key existence oracle; structured security logs retain
the operator cause
- add regressions across capsule inventory, agent/group views, pair
delegation, explicit issue-to-redeem scope preservation, malformed
allow/deny persistence rejection, broad-scope minting, audit outcomes,
stale-load publication, cross-principal cache isolation, and outward
denial opacity

## Verification

- `cargo fmt --all -- --check`
- `git diff --check`
- `cargo metadata --locked --offline --no-deps --format-version 1`
- deterministic snapshot regressions authorize, revoke and invalidate,
prove a later request fails, then prove the in-flight request keeps the
authority already audited as allowed
- focused regressions cover no-device, `Full`, scoped allow/deny,
malformed explicit patterns, unknown, revoked, concurrent stale-profile,
cross-principal invalidation, no-escalation audit outcomes, and
denial-reason opacity
- compilation, tests, Clippy, and platform coverage run in repository CI

## Checklist

- [x] Linked to an issue
- [x] CHANGELOG.md updated under `[Unreleased]`
joshuajbouw added a commit to astrid-runtime/astrid that referenced this pull request Jul 15, 2026
## Linked Issue

Closes #1239

Refs #1237 and astrid-runtime/rfcs#36.

## Summary

Carries the authenticated device into capsule dispatch so execution and
inventory use the same live authority already enforced at the kernel
boundary. The change is generic to Astrid and introduces no
product-specific role or trust path.

## Changes

- resolve one principal/profile/group/device snapshot per stamped event
- reject disabled, malformed, unknown, and revoked identities before
matching or grant signalling
- use device-effective `capsule:list` only for global describe
visibility
- activate exact device-effective `capsule:access:any` for unrestricted
execution and candidate expansion
- preserve exact per-principal capsule grants, no-device/`Full`
behavior, and principal-less lifecycle dispatch
- emit one `GrantRequired` only for a valid in-view denied match
- preserve legacy dispatch when no access resolver is installed
- preserve merged authority-cache refresh, pairing preflight, audit
classification, and caller-boundary audit behavior
- require base `self:auth:pair` authority for pair issuance while
retaining the additional `self:auth:pair:admin` gate and elevated audit
classification for Full, preset-Full, and explicit-universal scopes
- add a dedicated device-scope dispatch regression suite

## Verification

- restacked onto `main` after #1196, #1238, and #1257 merged
- signed head: `57ec31340db1a1749d5f137a25f4352bb6cb401f`
- `cargo fmt --all -- --check`
- `git diff --check origin/main...HEAD`
- `cargo test --locked -p astrid-capsule` — 563 passed
- `cargo test --locked -p astrid-kernel
admin_cap_without_base_pair_capability_cannot_issue_full_token --
--nocapture`
- `cargo clippy --locked -p astrid-capsule -p astrid-kernel
--all-features --tests -- -D warnings`
- replacement GitHub CI is the complete integration and execution gate

## Checklist

- [x] Linked to an issue
- [x] CHANGELOG.md updated under `[Unreleased]`

No release is created or published by this change.
joshuajbouw added a commit to astrid-runtime/astrid that referenced this pull request Jul 15, 2026
## Linked Issue

Closes #1235

Refs #1228, #1233, #1234, and astrid-runtime/rfcs#36.

## Summary

Populates the passive 51-entry capability-registry revision 1 on top of
the primitives merged in #1234. Existing authorization, wildcard
evaluation, persisted state, socket behavior, and wire contracts remain
unchanged.

## Changes

- bind all 51 kernel entries to fixed scope, ordered target kinds,
delegability, privileged status, source, and display danger
- expose the typed revision 1 manifest while keeping the raw ID set
private
- freeze all 51 BLAKE3 entry digests and the aggregate manifest digest
- freeze revision 1 danger metadata independently from the mutable live
capability catalog
- preserve current catalog scope and danger metadata
- require kernel and admin request mappings to resolve to a registered
entry
- complete kernel and admin request-variant inventories
- share one complete admin-request test inventory across the mapping and
registry suites
- append new public error variants without changing existing implicit
discriminants
- classify every revision 1 ID as primary, secondary,
token-authenticated, dormant, or mapping-only
- classify `capsule:access:any` as enforced secondary authority now that
capsule dispatch enforces it
- use immutable registry-revision terminology throughout the public API
and errors

Revision 1 never silently expands. A capability added to the catalog, an
inventoried request mapping, or the capsule-side enforcement constants
covered here fails its registry check until a new revision is
introduced. An entirely new enforcement site must add its owning-crate
registry assertion.

## Verification

- exact signed head: `aaf8952f08c2441b217fec60d90de4a40c922754`
- restacked directly on merged `main` at
`bed06ad476f6e58a5ee56331118da497d121705e`
- all 15 commits have good GPG signatures from Joshua J. Bouw
- `cargo fmt --all -- --check`
- `git diff --check origin/main...HEAD`
- `cargo test --locked -p astrid-core capability_registry` (20 passed)
- `cargo test --locked -p astrid-kernel capability_catalog_tests` (4
passed)
- `cargo test --locked -p astrid-kernel
empty_agent_modify_keeps_existing_wire_and_authority_mapping` (1 passed)
- `cargo test --locked -p astrid-capsule
unrestricted_capsule_access_enforcement_id_is_registered`
- `cargo test --locked -p astrid-capsule
secondary_enforcement_ids_are_registered`
- `cargo test --locked -p astrid-core -p astrid-capsule -p
astrid-kernel` (274 core, 564 capsule, and 258 kernel tests passed; the
macOS socket group was rerun outside the sandbox)
- `cargo clippy --locked -p astrid-core -p astrid-capsule -p
astrid-kernel --all-features -- -D warnings`
- range-diff confirms the registry commits are preserved while the
previously stacked authority fixes are now supplied by merged `main`
- the skipped historical integration-merge correction is retained:
`capsule:access:any` is live secondary authority, not dormant metadata
- the production capsule constants for unrestricted access, audit
firehose, and resource exemptions are pinned to their intended exact IDs
and checked directly against revision 1; kernel request inventory is
shared between mapping and registry suites

## Compatibility

The registry remains passive. This PR does not activate a registry
evaluator, change authorization behavior, migrate persisted state, or
alter the existing wire shape. Revision 1 is an immutable description of
the enforcement surface that already exists.

## Checklist

- [x] Linked to an issue
- [x] CHANGELOG.md updated under `[Unreleased]`
- [x] No evaluator activation or persisted-state migration

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
joshuajbouw added a commit to astrid-runtime/astrid that referenced this pull request Jul 15, 2026
## Linked Issue

Closes #1241

Refs #1237 and astrid-runtime/rfcs#36.

## Summary

Replaces gateway-side audit authority inference with the kernel's live
principal/device capability evaluator. Historical and streaming audit
views now narrow through the same runtime policy, with no AOS-specific
role, key exposure or caller-selected identity.

## Changes

- add an additive borrowed `CapabilityCheck` constructor while
preserving the existing constructor and owned errors
- expose one doc-hidden boolean kernel evaluator for a principal,
optional device and capability
- inject the in-process evaluator through the gateway runner and an
additive embedded-router builder; the default router remains self-only
- evaluate historical audit authority after the asynchronous log read
and at every pagination record boundary
- re-evaluate `audit:read_all` for every live SSE event
- preserve self visibility after malformed, unknown, revoked, disabled,
group-revoked or scope-denied authority
- keep raw audit cursor positions stable while live principal narrowing
filters same-second batches
- bind new cursors to immutable audit-entry identity and effective
visibility scope so same-second appends cannot shift the page boundary
and widening or a principal change requires a restart
- reject unsafe scope changes before visible, hidden and cursor-skipped
rows instead of returning a partial page or false end-of-results
- accept legacy timestamp/raw-offset cursors only for the unambiguous
default self view
- suppress continuation cursors at true physical EOF while retaining a
scope-bound cursor when hidden rows make a later widening unsafe
- pin the gateway's live `audit:read_all` enforcement constant and
require it to resolve through capability-registry revision 1
- retain the pair-device terminal audit distinctions already present on
`main`
- add a race-free pre-bound listener entrypoint for the real HTTP SSE
regression
- expose the same live evaluator boundary to trusted in-process router
embeddings without exposing the internal probe type
- document the connect-info make-service requirement for both public
router builders so unauthenticated redeem throttling keeps the real peer
address, and make the default builder's self-only audit policy explicit
- document the additive runner/router migration path for co-located
callers that need audit firehose visibility
- accept hyphen-leading opaque invite tokens in both CLI redeem and
revoke paths so every base64url token the runtime issues remains usable

## Verification

- exact signed head: `bf3eb251164f1c20e47fb9f3e841677d9efecc02`
- all 20 commits above `d85fb10663c21d08fc43d39d939cfbb5a023cafd` have
good Joshua committer signatures
- `cargo fmt --all -- --check`
- `git diff --check`
- `cargo doc -p astrid-gateway --no-deps`
- `cargo test -p astrid-gateway` — 160 unit, 7 CORS, 10 models and 18
router tests pass
- `cargo test -p astrid-kernel runtime_policy_tests` — 8 tests pass
- focused audit pagination suite — 17 tests pass, including same-second
insertion, missing-anchor and pre-anchor authority-widening regressions
- focused registry enforcement assertion — 1 test passes
- `cargo clippy -p astrid-gateway -p astrid-kernel --all-targets -- -D
warnings`
- all 12 production-root CLI parser tests pass, including redeem and
revoke with a generated-token shape beginning in `-`
- `cargo clippy -p astrid --all-targets -- -D warnings`
- the replacement GitHub matrix is the execution gate for the combined
workspace

## Compatibility

The public HTTP shape, WIT contracts, persisted authority, gateway state
and existing runner entrypoints remain unchanged. The new kernel
evaluator is boolean and doc-hidden. The internal typed gateway probe
remains private; trusted in-process embedders receive an additive
generic callback builder with the same fail-closed policy boundary.

When #1244 is refreshed after this PR, its workspace-aware gateway
runner must retain this live capability probe across normal, TLS and
pre-bound listener paths.

## Checklist

- [x] Linked to an issue
- [x] `CHANGELOG.md` updated under `[Unreleased]`

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant