Skip to content

feat(policy): ACL egress verifier — defense-in-depth re-check (Step 6.4) - #151

Merged
officialCodeWork merged 1 commit into
mainfrom
build/phase-6/step-6.4-acl-egress-verifier
Jun 7, 2026
Merged

feat(policy): ACL egress verifier — defense-in-depth re-check (Step 6.4)#151
officialCodeWork merged 1 commit into
mainfrom
build/phase-6/step-6.4-acl-egress-verifier

Conversation

@officialCodeWork

Copy link
Copy Markdown
Owner

Summary

Step 6.4 adds the ACL egress verifier — a post-retrieval re-check that backstops the Step 6.3 ACL push-down. The push-down is the primary, source-side layer (re-implemented by each backend translator); this is the independent second layer the tests/policy/coverage.py linter docstring already anticipated.

  • AclEgressVerifier.verify(ctx, refs) (rag_policy.egress) keeps only returned ChunkRefs whose acl_labels overlap the principal's — the same overlap semantics as the push-down, so it's a no-op on a correctly-filtered result and only ever acts on a genuine leak. Reads ChunkRef.acl_labels (populated by every backend regardless of the filter applied) → no re-hydration, O(results) set-intersection.
  • Independent of the PolicyEngine (consults only ctx.principal.acl_labels), so a filter-translation bug, a backend that ignores the predicate, or a path wired without the engine can't leak an over-privileged chunk.
  • Layered at the retrieval router boundary via a thin SupportsRoute wrapper (AclEgressVerifyingRouter, kept in the gateway so rag-retrieval stays free of rag-policy) around app.state.retrieval_router — query / retrieve / corpus / OpenAI / agent all inherit it, a layer above HybridRetriever.
  • Opt-in, on-by-default behind ACL: cfg.acl.verify_egress (default true) gated on cfg.acl.enabled; set false to run the push-down alone. app.state.acl_verify_egress reports the effective state.
  • acl.egress_violation event (EVT_ACL_EGRESS_VIOLATION, error level, PII-free: tenant / principal / counts / dropped chunk ids) on a caught leak — the unexpected push-down failure, distinct from 6.3's expected acl.egress_denied. A clean pass is silent.

Scope / deferred: covers the gateway retrieval surfaces; trusts the labels the backend reports on each ChunkRef (catching a mislabelling backend needs authoritative re-hydration — deferred); per-tenant/per-label violation metrics deferred to the 6.x governance dashboards.

Tests & gates

  • ~21 new tests: verifier unit (tests/policy/test_acl_egress.py), red-team zero-violation-rate gate (tests/redteam/test_acl_egress_verifier.py — bypassed push-down + a leaky backend → verifier drops every violation), gateway wiring + behavioral drop (apps/gateway/tests/test_acl.py), event-schema registry.
  • Full suite: 2299 passed, 1 skipped. Green: ruff, mypy --strict (296 files), RAG001, policy-coverage linter, log-schema/event-registry, config schema-drift (dist/rag.schema.* regenerated for verify_egress).

Documentation

Test plan

  • uv run pytest tests/ packages/ -m "not integration" — 2299 passed, 1 skipped
  • uv run ruff check + ruff format --check — clean
  • uv run mypy packages/ apps/gateway/ — no issues (296 files)
  • RAG001 + policy-coverage + event-schema + config schema-drift gates
  • Red-team gate proves zero escaped-violation rate with the push-down bypassed

🤖 Generated with Claude Code

Add a post-retrieval ACL re-check that backstops the Step 6.3 push-down.
AclEgressVerifier.verify(ctx, refs) keeps only returned ChunkRefs whose
acl_labels overlap the principal's — same overlap semantics as the push-down
(no-op on correct results), reading ChunkRef.acl_labels (no re-hydration), and
independent of the PolicyEngine — so a filter-translation bug, a backend that
ignores the predicate, or a path wired without the engine cannot leak an
over-privileged chunk past the boundary.

Wired at the gateway as a SupportsRoute wrapper (AclEgressVerifyingRouter)
around app.state.retrieval_router, so query/retrieve/corpus/OpenAI/agent all
inherit it a layer above HybridRetriever. Opt-in via cfg.acl.verify_egress
(default on, gated by cfg.acl.enabled). Emits acl.egress_violation on a caught
leak (PII-free: ids + counts). A red-team gate bypasses the push-down (no engine
+ leaky backend) and proves a zero escaped-violation rate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@officialCodeWork
officialCodeWork merged commit 8cf064f into main Jun 7, 2026
12 of 17 checks passed
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