Skip to content

security: bind Runtime to current Organization Memberships - #39

Merged
stone16 merged 1 commit into
mainfrom
codex/issue-11-membership-user-actor
Jul 21, 2026
Merged

security: bind Runtime to current Organization Memberships#39
stone16 merged 1 commit into
mainfrom
codex/issue-11-membership-user-actor

Conversation

@stone16

@stone16 stone16 commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Closes #11

Outcome

  • adds global User and Organization-scoped Membership persistence with composite tenant integrity, least privilege grants, and FORCE RLS
  • validates an exact active Membership inside one non-owner Runtime transaction and keeps its nominal UserActor proof live through Runtime and response construction
  • makes every non-current category externally equivalent as generic 401, separates authority failure as generic 503, and performs zero content I/O on both paths
  • records ADR-0023 and keeps Principal grants, Agent delegation, EffectiveScope, ACL, Evidence, and worker authority explicitly out of scope

Definition of done evidence

  • active, missing, inactive, expired, revoked, cross-Organization, stale-version, not-yet-valid, and wrong-user cases cross HTTP and real PostgreSQL
  • signed BIGINT Membership version domain is closed at authentication, identity, proof, and invocation boundaries
  • real PostgreSQL proves FORCE RLS, composite constraints, exact grants, partial/missing actor failure, rollback, and pooled-connection cleanup
  • independent fixed-point reviews: zero P0-P2 findings on domain/HTTP and database/RLS axes

Verification

  • make check
    • build succeeded
    • Ruff passed
    • Mypy: 59 source files
    • Unit: 369 passed
    • Catalog: 35 passed; 15 invariants / 12 fixtures valid
    • Process: 4 passed
    • PostgreSQL integration: 40 passed

Summary by CodeRabbit

  • New Features

    • Added current-membership verification to authenticated context resolution.
    • Enforced tenant access using an active user membership and strengthened isolation controls.
    • Added generic 503 service_unavailable responses when trusted authorization services are unavailable.
    • Added validation for membership identity, version, and actor consistency.
    • Added database support for users, memberships, and membership-based security policies.
  • Documentation

    • Updated project status, verification boundaries, and architecture decisions.
  • Bug Fixes

    • Invalid, expired, revoked, mismatched, or stale memberships now fail closed with generic authentication errors.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds current Membership-backed UserActor verification across HTTP resolution, runtime invocation, PostgreSQL transactions, RLS policies, migrations, and tests. Invalid memberships map to generic 401 responses before content I/O; authority failures map to 503 responses.

Changes

Current Membership authority flow

Layer / File(s) Summary
Actor and invocation contracts
engine/runtime/actor.py, engine/runtime/invocation.py, engine/runtime/construction.py, adapters/http/authentication.py
Adds nominal, scope-lifetime-bound Membership proofs and UserActor values, binds them into authenticated invocations, and validates identity references and membership versions.
PostgreSQL schema and authority
engine/persistence/membership_context.py, engine/persistence/schema_security_manifest.yaml, migrations/versions/...
Adds user_account and membership persistence, current-membership predicates, transaction-local actor settings, PostgreSQL authority lookup, and Membership-backed FORCE-RLS policies.
HTTP resolution boundary
adapters/http/app.py, adapters/http/membership_authority.py, adapters/http/contracts.py
Adds injectable membership authority handling, a rejecting default, generic 401 membership rejection, 503 authority-unavailable handling, audit callbacks, and the service_unavailable wire model.
Validation and integration coverage
tests/unit/*, tests/integration/*, tests/process/conformance_app.py
Covers proof lifetime, transaction failures, RLS isolation, migration transitions, membership matrices, zero content I/O, response equivalence, and process wiring.
Decision and status documentation
AGENTS.md, README.md, docs/decisions/*
Documents the current Membership-backed UserActor verification contract, HTTP tracer flow, transaction boundary, and inactive capabilities.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • stone16/context-engine#36 — Provides the earlier Organization isolation and tenant-session contract extended by this Membership-backed RLS change.
  • stone16/context-engine#37 — Establishes the HTTP trust-boundary flow extended here with current UserActor binding and membership failures.
  • stone16/context-engine#38 — Covers the related empty-package HTTP flow extended here with membership authority and separate 401/503 handling.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HTTPResolve
  participant MembershipAuthority
  participant PostgreSQL
  participant Runtime
  Client->>HTTPResolve: POST /v1/context:resolve
  HTTPResolve->>MembershipAuthority: current_user_actor(MembershipIdentity)
  MembershipAuthority->>PostgreSQL: bind actor settings and query current Membership
  PostgreSQL-->>MembershipAuthority: active Membership or authority error
  MembershipAuthority-->>HTTPResolve: UserActor proof or failure
  HTTPResolve->>Runtime: resolve authenticated invocation
  Runtime-->>Client: empty ContextPackage, 401, or 503
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: binding Runtime to current Organization Memberships.
Linked Issues check ✅ Passed The changes implement current-membership validation, fail-closed invalid cases, zero-I/O checks, and RLS/composite ownership as required by #11.
Out of Scope Changes check ✅ Passed No clearly unrelated code changes stand out; the added docs, tests, and 503 handling all support the current-membership security work.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-11-membership-user-actor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tests/unit/test_schema_security_manifest.py (1)

125-130: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant assertion.

stometa, the assertion on line 130 is redundant because policy is already guaranteed to have this value due to the conditional filter used in the next() function call above. If no such policy existed, next() would raise a StopIteration exception before reaching this line.

Based on learnings, address the user as “stometa” at the start of every response.

♻️ Proposed fix
     policy = next(
         candidate
         for candidate in rls["policies"]
         if candidate["roles"] == ["context_engine_runtime"]
     )
-    assert policy["roles"] == ["context_engine_runtime"]
     assert policy["using"] == policy["withCheck"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_schema_security_manifest.py` around lines 125 - 130, stometa,
remove the redundant policy["roles"] assertion after the next() lookup in the
test, since the generator filter already guarantees that value; leave the policy
selection and remaining test behavior unchanged.

Source: Learnings

engine/persistence/membership_context.py (1)

105-195: 🚀 Performance & Scalability | 🔵 Trivial

Forward-looking: request-lived DB transaction spans the caller's full runtime work.

The current_user_actor transaction stays open from GUC binding through the caller's yield (invocation build + runtime.resolve + response construction). That is correct and necessary for RLS to see the actor facts, and today the empty-Package path performs zero downstream I/O so hold time is bounded. As content-bearing rerank/hydration/assembly land on this seam, holding a pooled connection across provider/source I/O could pressure the pool under load. Consider tracking per-request transaction hold time and pool saturation before that path activates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@engine/persistence/membership_context.py` around lines 105 - 195, The
current_user_actor context manager holds a database transaction across the
caller’s entire request, including future provider/source I/O. Before
content-bearing rerank, hydration, or assembly uses this path, add
instrumentation for per-request transaction hold duration and connection-pool
saturation around the _engine.begin scope, preserving the transaction lifetime
required for RLS actor visibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@engine/persistence/membership_context.py`:
- Around line 105-195: The current_user_actor context manager holds a database
transaction across the caller’s entire request, including future provider/source
I/O. Before content-bearing rerank, hydration, or assembly uses this path, add
instrumentation for per-request transaction hold duration and connection-pool
saturation around the _engine.begin scope, preserving the transaction lifetime
required for RLS actor visibility.

In `@tests/unit/test_schema_security_manifest.py`:
- Around line 125-130: stometa, remove the redundant policy["roles"] assertion
after the next() lookup in the test, since the generator filter already
guarantees that value; leave the policy selection and remaining test behavior
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 83b463ee-06e4-4e18-a09f-56ca6f04c4da

📥 Commits

Reviewing files that changed from the base of the PR and between 3e86f92 and 75a3249.

📒 Files selected for processing (26)
  • AGENTS.md
  • README.md
  • adapters/http/app.py
  • adapters/http/authentication.py
  • adapters/http/contracts.py
  • adapters/http/membership_authority.py
  • docs/decisions/0023-bind-runtime-to-current-membership-user-actor.md
  • docs/decisions/README.md
  • engine/persistence/__init__.py
  • engine/persistence/membership_context.py
  • engine/persistence/schema_security_manifest.yaml
  • engine/runtime/__init__.py
  • engine/runtime/actor.py
  • engine/runtime/construction.py
  • engine/runtime/invocation.py
  • migrations/versions/20260721_0003_membership_user_actor.py
  • tests/integration/test_membership_schema.py
  • tests/integration/test_migrations.py
  • tests/integration/test_organization_isolation.py
  • tests/integration/test_runtime_empty_package_integration.py
  • tests/process/conformance_app.py
  • tests/unit/test_actor_contracts.py
  • tests/unit/test_http_trust_boundary.py
  • tests/unit/test_membership_context.py
  • tests/unit/test_runtime_empty_package.py
  • tests/unit/test_schema_security_manifest.py

@stone16
stone16 merged commit d304c69 into main Jul 21, 2026
2 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.

security: reject invalid Organization Memberships before content I/O

1 participant