feat(m7.2-C): migrate background paths to per-tenant pool #88

Closed
sharang wants to merge 1 commits from feat/m7.2c-background-paths into feat/m7.2b-handlers-tenant-scoped
Owner

Summary

Closes the loop on M7.2 isolation for background paths that don't have a JWT context: scheduler, webhooks, and the agent's run_scan / run_pr_review helpers all now take a tenant_id at the boundary and resolve to a tenant-scoped Database via db_pool.for_tenant_id(...).

Internal orchestrators (PipelineOrchestrator, PentestOrchestrator) and pipeline helpers were already DB-agnostic — they take db: Database at construction and don't care which tenant it points to. So the migration scope is purely the boundary code.

Stacked on PR-B (#87) — diff shown is only C's changes.

Changes

  • DatabasePool::for_tenant_id(&str) — same as for_tenant but accepts a bare tenant_id string. Background paths don't have a full TenantContext. for_tenant is now a thin wrapper that delegates.
  • agent.run_scan(tenant_id, repo_id, trigger) — pulls the tenant database before constructing the PipelineOrchestrator. Was: run_scan(repo_id, trigger) reading agent.db.
  • agent.run_pr_review(tenant_id, repo_id, ...) — same shape.
  • Webhook routes: /webhook/{tenant_id}/{platform}/{repo_id}. Tenant is part of the URL path because webhooks arrive without a JWT — they're authenticated via per-repo HMAC, not the tenant gate. The dashboard surfaces the full per-tenant URL when the repo is registered. All three handlers (gitea, github, gitlab) updated.
  • scheduler.rs — iterates tenants from $SCHEDULER_TENANT_IDS (comma-separated env), or DEV_TENANT_ID's default of dev. Both scan_all_repos and monitor_cves now run once per configured tenant. PR-D will replace this static config with a pull from the tenant-registry.
  • api/handlers/repos.rs::trigger_scan now passes tenant.0.tenant_id into run_scan.

What's unchanged because it didn't need to change

  • PipelineOrchestrator, PentestOrchestrator — take db: Database at construction; tenant-DB-agnostic by design. The caller picks the tenant DB. (Per-tenant DB pattern means the orchestrator never sees a tenant_id directly — it just writes to whichever DB it was handed.)
  • pipeline/{dedup,graph_build,issue_creation,sbom/mod}.rs, pentest/{context,report/html/*}.rs, trackers/jira.rs, llm/triage.rs — take &Database or &mongodb::Database as args, transitively tenant-scoped via the caller.

Grep proof:

$ grep -rn "agent\.db\b" compliance-agent/src/ | grep -v "agent\.db_pool"
(empty)

Test plan

  • cargo fmt --all -- --check clean
  • cargo clippy --workspace --exclude compliance-dashboard -- -D warnings clean
  • cargo test -p compliance-core --lib — 7 pass
  • cargo test -p compliance-agent --lib — 228 pass
  • cargo test -p compliance-agent --test tenant_isolation — 5 pass
  • cargo test -p compliance-agent --test tenant_status_middleware — 6 pass

What PR-D does

  • Drop the transitional agent.db field — no remaining call sites (verified by grep above).
  • main.rs / TestServer stop constructing the legacy Database; only the pool remains.
  • Add cross-tenant admin helpers (list_tenants, drop_tenant_db) on the pool for offboarding flows.
  • Replace SCHEDULER_TENANT_IDS env with a pull from the tenant-registry so the scheduler picks up new tenants automatically.

🤖 Generated with Claude Code

## Summary Closes the loop on **M7.2 isolation for background paths** that don't have a JWT context: scheduler, webhooks, and the agent's `run_scan` / `run_pr_review` helpers all now take a `tenant_id` at the boundary and resolve to a tenant-scoped `Database` via `db_pool.for_tenant_id(...)`. Internal orchestrators (`PipelineOrchestrator`, `PentestOrchestrator`) and pipeline helpers were already DB-agnostic — they take `db: Database` at construction and don't care which tenant it points to. So the migration scope is purely the boundary code. **Stacked on PR-B (#87)** — diff shown is only C's changes. ## Changes - **`DatabasePool::for_tenant_id(&str)`** — same as `for_tenant` but accepts a bare `tenant_id` string. Background paths don't have a full `TenantContext`. `for_tenant` is now a thin wrapper that delegates. - **`agent.run_scan(tenant_id, repo_id, trigger)`** — pulls the tenant database before constructing the `PipelineOrchestrator`. Was: `run_scan(repo_id, trigger)` reading `agent.db`. - **`agent.run_pr_review(tenant_id, repo_id, ...)`** — same shape. - **Webhook routes**: `/webhook/{tenant_id}/{platform}/{repo_id}`. Tenant is part of the URL path because webhooks arrive **without** a JWT — they're authenticated via per-repo HMAC, not the tenant gate. The dashboard surfaces the full per-tenant URL when the repo is registered. All three handlers (gitea, github, gitlab) updated. - **`scheduler.rs`** — iterates tenants from `$SCHEDULER_TENANT_IDS` (comma-separated env), or `DEV_TENANT_ID`'s default of `dev`. Both `scan_all_repos` and `monitor_cves` now run once per configured tenant. **PR-D** will replace this static config with a pull from the tenant-registry. - **`api/handlers/repos.rs::trigger_scan`** now passes `tenant.0.tenant_id` into `run_scan`. ## What's unchanged because it didn't need to change - `PipelineOrchestrator`, `PentestOrchestrator` — take `db: Database` at construction; tenant-DB-agnostic by design. The caller picks the tenant DB. (Per-tenant DB pattern means the orchestrator never sees a tenant_id directly — it just writes to whichever DB it was handed.) - `pipeline/{dedup,graph_build,issue_creation,sbom/mod}.rs`, `pentest/{context,report/html/*}.rs`, `trackers/jira.rs`, `llm/triage.rs` — take `&Database` or `&mongodb::Database` as args, transitively tenant-scoped via the caller. Grep proof: ``` $ grep -rn "agent\.db\b" compliance-agent/src/ | grep -v "agent\.db_pool" (empty) ``` ## Test plan - [x] `cargo fmt --all -- --check` clean - [x] `cargo clippy --workspace --exclude compliance-dashboard -- -D warnings` clean - [x] `cargo test -p compliance-core --lib` — 7 pass - [x] `cargo test -p compliance-agent --lib` — 228 pass - [x] `cargo test -p compliance-agent --test tenant_isolation` — 5 pass - [x] `cargo test -p compliance-agent --test tenant_status_middleware` — 6 pass ## What PR-D does - Drop the transitional `agent.db` field — no remaining call sites (verified by grep above). - `main.rs` / `TestServer` stop constructing the legacy `Database`; only the pool remains. - Add cross-tenant admin helpers (`list_tenants`, `drop_tenant_db`) on the pool for offboarding flows. - Replace `SCHEDULER_TENANT_IDS` env with a pull from the tenant-registry so the scheduler picks up new tenants automatically. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sharang added 1 commit 2026-06-17 13:01:11 +00:00
feat(m7.2-C): migrate background paths to per-tenant pool
CI / Check (pull_request) Successful in 10m33s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
0f6dd1135e
Closes the loop on M7.2 isolation for paths that don't have a JWT
context: scheduler, webhooks, and the agent's `run_scan` / `run_pr_review`
helpers all now take a `tenant_id` at the boundary and resolve to a
tenant-scoped `Database` via `db_pool.for_tenant_id(...)`. Internal
orchestrators (PipelineOrchestrator, PentestOrchestrator) and pipeline
helpers were already DB-agnostic — they take `db: Database` at
construction and don't care which tenant it points to.

Changes
- DatabasePool::for_tenant_id(&str) — same as for_tenant but accepts
  a bare tenant_id. Background paths don't have a full TenantContext.
  for_tenant is now a thin wrapper that delegates.
- agent.run_scan(tenant_id, repo_id, trigger) — pulls the tenant
  database before constructing the PipelineOrchestrator. Was:
  run_scan(repo_id, trigger) reading agent.db.
- agent.run_pr_review(tenant_id, repo_id, ...) — same shape.
- Webhook routes change: /webhook/{tenant_id}/{platform}/{repo_id}.
  Tenant is part of the URL path because webhooks arrive without a
  JWT — they're authenticated via per-repo HMAC, not the tenant gate.
  The dashboard surfaces the full per-tenant URL when the repo is
  registered. All three handlers (gitea, github, gitlab) updated.
- scheduler.rs — iterates tenants from $SCHEDULER_TENANT_IDS
  (comma-separated env), or DEV_TENANT_ID's `dev` default. Both
  scan_all_repos and monitor_cves now run once per configured
  tenant. M7.2-D will replace this static config with a pull from
  the tenant-registry.
- api/handlers/repos.rs::trigger_scan now passes tenant.0.tenant_id.

What's unchanged because it didn't need to change
- PipelineOrchestrator, PentestOrchestrator: take `db: Database` at
  construction — they're tenant-DB-agnostic by design. The caller
  picks the tenant DB.
- pipeline/{dedup,graph_build,issue_creation,sbom/mod}.rs,
  pentest/{context,report/html/*}.rs, trackers/jira.rs, llm/triage.rs:
  take `&Database` or `&mongodb::Database` as args, transitively
  tenant-scoped via the caller.

Test plan
- cargo fmt --all clean
- cargo clippy --workspace --exclude compliance-dashboard
  -- -D warnings clean
- cargo test -p compliance-core --lib — 7 pass
- cargo test -p compliance-agent --lib — 228 pass
- cargo test -p compliance-agent --test tenant_isolation — 5 pass
- cargo test -p compliance-agent --test tenant_status_middleware
  — 6 pass

What's left (PR-D)
- Drop the transitional agent.db field — no remaining call sites
  (verified by `grep -rn "agent\.db\b" compliance-agent/src`).
- main.rs / TestServer stop building the legacy Database; only the
  pool remains.
- Add cross-tenant admin helpers (list tenants, drop tenant DB) on
  the pool for offboarding flows.
- Pull tenants from the tenant-registry instead of an env var.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Author
Owner

Superseded — the M7.2 stack was inadvertently included in PR #90 squash-merge (5648291) on main. The dashboard PR was branched off this PR's descendant and its full diff swept into main as one squash commit. M7.2-A through M7.2-D are all live on main and in production. Closing without merging.

Superseded — the M7.2 stack was inadvertently included in PR #90 squash-merge (`5648291`) on main. The dashboard PR was branched off this PR's descendant and its full diff swept into main as one squash commit. M7.2-A through M7.2-D are all live on main and in production. Closing without merging.
sharang closed this pull request 2026-06-18 09:32:15 +00:00
CI / Check (pull_request) Successful in 10m33s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped

Pull request closed

Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sharang/compliance-scanner-agent#88