Skip to content

fix(gateway): enforce live audit authority#1242

Merged
joshuajbouw merged 20 commits into
mainfrom
agent/audit-device-attenuation
Jul 15, 2026
Merged

fix(gateway): enforce live audit authority#1242
joshuajbouw merged 20 commits into
mainfrom
agent/audit-device-attenuation

Conversation

@joshuajbouw

@joshuajbouw joshuajbouw commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

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

  • Linked to an issue
  • CHANGELOG.md updated under [Unreleased]

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens gateway audit visibility by replacing gateway-side inference of audit:read_all with a live, kernel-owned principal/device/capability evaluator. It unifies historical audit queries and SSE streaming under the same runtime policy, ensuring device revocation and scope narrowing take effect immediately without reconnecting.

Changes:

  • Add a borrowed CapabilityCheck::new_borrowed constructor and use it to implement a boolean Kernel::runtime_capability_allows evaluator.
  • Inject a gateway-private CapabilityProbe via request extensions; historical audit pagination and live SSE streams re-check audit:read_all through the probe.
  • Add targeted kernel, gateway, and integration coverage to ensure revocation/narrowing preserves self-visibility while suppressing cross-principal access.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/astrid-kernel/src/runtime_policy_tests.rs Adds focused regression tests for principal/device capability evaluation semantics.
crates/astrid-kernel/src/lib.rs Exposes a doc-hidden, boolean runtime capability evaluator used by the gateway/daemon.
crates/astrid-integration-tests/tests/gateway_e2e.rs Extends E2E coverage to validate live SSE narrowing after device revocation.
crates/astrid-gateway/src/routes/mod.rs Introduces router builder wiring to inject a capability probe via Extension.
crates/astrid-gateway/src/routes/events.rs Implements probe-backed audit:read_all evaluation and per-event re-evaluation for SSE.
crates/astrid-gateway/src/routes/audit.rs Applies probe-backed, post-read authority at query boundary and during record filtering.
crates/astrid-gateway/src/lib.rs Adds run_with_capability_probe* entrypoints and threads the probe into router construction.
crates/astrid-daemon/src/lib.rs Wires the gateway probe to the kernel’s live evaluator in the daemon runtime.
crates/astrid-capabilities/src/policy.rs Adds a borrowed constructor to avoid cloning principals for boolean-only checks.
CHANGELOG.md Documents the security-relevant change to audit authority evaluation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@joshuajbouw
joshuajbouw marked this pull request as ready for review July 14, 2026 18:13
@joshuajbouw
joshuajbouw requested a review from Copilot July 14, 2026 18:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

@joshuajbouw
joshuajbouw force-pushed the agent/device-attenuation-secondary-views branch from 5b5cbcd to b93409c Compare July 15, 2026 01:33
@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from 30b506b to 9b6a0d2 Compare July 15, 2026 01:34
@joshuajbouw
joshuajbouw changed the base branch from agent/device-attenuation-secondary-views to main July 15, 2026 01:39
@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 01d9e49

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 01d9e49

Addressed a historical audit pagination bug found while reviewing the exact head in f615605. The audit cursor now advances in stable pre-authorization order so live authority narrowing can’t skip or duplicate same-second self entries across pages.

@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from f615605 to 4b1d30b Compare July 15, 2026 08:11
@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 4b1d30b

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 4b1d30b

Addressed a historical audit cursor hole in 6d600f6. If audit:read_all widened between pages, the raw same-second cursor could silently skip newly visible rows; cursors now carry their effective visibility scope and fail closed when that scope widens or changes incompatibly, while the intended narrowing path still paginates cleanly.

@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from 6d600f6 to 05842ae Compare July 15, 2026 08:33
@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 05842ae

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 05842ae

Addressed in d5ab282. Historical audit pagination now clips the page when live visibility re-widens or changes incompatibly mid-request, and it returns a scope-bound continuation cursor instead of silently skipping rows; added a focused regression test for the all → self-only → all case.

@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from d5ab282 to 8f168b4 Compare July 15, 2026 09:02
@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 8f168b4

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 8f168b4

Addressed in 5b932af. The exact-head review caught the current Clippy failure in the new audit pagination regression: the test cursor timestamp inferred as i64, which no longer matched the tightened (Option<u64>, usize, Option<...>) cursor tuple. I fixed the test typing, used the signed conversion Clippy expects for timestamp_opt, and scoped clippy::too_many_lines to the long regression test so the gateway audit test target and cargo clippy -p astrid-gateway --all-targets -- -D warnings both pass.

@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from 5b932af to edde792 Compare July 15, 2026 09:19
@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from ea12413 to b477f65 Compare July 15, 2026 15:20
@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head b477f65

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 92bd7a0

Addressed in ea12413.

Direct router embedders can now use routes::build_with_capability_probe(...) to preserve live audit:read_all evaluation, while routes::build(...) stays self-only by default. Re-verified with cargo fmt --all -- --check, cargo test -p astrid-gateway routes::audit::tests:: -- --nocapture, cargo test -p astrid-integration-tests kernel_and_gateway_boot_against_shared_home --test gateway_e2e -- --nocapture, and cargo clippy -p astrid-gateway -p astrid-kernel --all-targets -- -D warnings.

@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 39720da

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head b477f65

Addressed in 8db2484.

Documented that embedded routers built with routes::build_with_capability_probe(...) must be served via into_make_service_with_connect_info::<SocketAddr>(), so the unauthenticated redeem endpoints keep the peer-address extension they require for per-IP throttling. Re-verified with cargo fmt --all -- --check, cargo test -p astrid-gateway --test router public_router_builder_accepts_live_capability_probe -- --nocapture, cargo test -p astrid-gateway routes::audit::tests:: -- --nocapture, and cargo test -p astrid-integration-tests kernel_and_gateway_boot_against_shared_home --test gateway_e2e -- --nocapture.

@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from 8db2484 to 0a38bfe Compare July 15, 2026 15:42

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 39720da

Addressed in 8db2484.

Embedded routers built with routes::build_with_capability_probe(...) now document that they must be served via into_make_service_with_connect_info::<SocketAddr>(), so the unauthenticated redeem endpoints keep the peer-address extension required for per-IP throttling. Re-verified with cargo test -p astrid-gateway --test router public_router_builder_accepts_live_capability_probe -- --nocapture and cargo test -p astrid-gateway --test router pair_device_redeem_is_rate_limited_per_ip -- --nocapture.

@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 0a38bfe

@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head 1f8f5a5

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 0a38bfe

Addressed in afe04d2 and f994546.

I added a changelog migration note clarifying that co-located callers must use astrid_gateway::run_with_capability_probe(...) or routes::build_with_capability_probe(...) to retain firehose visibility, and I deduped the router builder docs around the connect-info requirement.

Re-verified with cargo fmt --all -- --check, cargo test -p astrid-gateway --test router public_router_builder_accepts_live_capability_probe -- --nocapture, and a clean final validation pass.

@joshuajbouw
joshuajbouw force-pushed the agent/audit-device-attenuation branch from f994546 to bf3eb25 Compare July 15, 2026 16:05
@joshuajbouw

Copy link
Copy Markdown
Contributor Author

@copilot review exact head bf3eb25

Copilot AI commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@copilot review exact head 1f8f5a5

Reviewed the exact head and didn’t find any additional significant issues. Focused gateway audit, router, kernel runtime-policy, and CLI parser coverage are passing locally, and the current GitHub checks are green or still in progress.

@joshuajbouw
joshuajbouw merged commit f255e5f into main Jul 15, 2026
27 of 28 checks passed
Copilot stopped work on behalf of joshuajbouw due to an error July 15, 2026 16:20
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.

Enforce live device policy on audit views

3 participants