Replaces the M7.2-C static `SCHEDULER_TENANT_IDS` env enumeration with
a live query to the tenant-registry at every tick. New tenants get
picked up without an agent restart; the env stays as the fallback
when the registry is unreachable so the scheduler is never silenced
by a registry outage.
Resolution order
1. agent.config.tenant_registry_url → GET <url>/v1/tenants
- 5s timeout (kept short — we'd rather fall back than block the
tick)
- Frozen and Archived tenants filtered out (the M7.1 status gate
would 402/410 them anyway, no point scanning their repos)
- Accepts either {"id"} or {"tenant_id"} for forward compatibility
with whatever shape the registry settles on
2. SCHEDULER_TENANT_IDS env (comma-separated) — fallback when the
registry URL is unset OR the fetch fails OR the parsed response is
empty. Each failure mode logs a warn with the url so operators see
the problem.
3. DEFAULT_SCHEDULER_TENANT_ID ("dev") — last-ditch fallback so a
bare `cargo run` against a clean Mongo still scans the dev tenant.
Why each tick instead of caching
- Tick frequency is every few hours (scan_schedule default
"0 0 */6 * * *"). The registry call is at most 4 times a day per
agent — cheap.
- Caching introduces a staleness window for newly provisioned
tenants. The whole point of registry integration is to pick them
up fast.
Startup log
- Includes "tenant source=tenant-registry" or "env" so operators can
tell at a glance which mode the scheduler is in.
Test plan
- cargo fmt --all clean
- cargo clippy -p compliance-agent -- -D warnings clean
- cargo test -p compliance-agent --lib — 232 pass (+3 new):
* filter_active_keeps_running_skips_frozen_archived
* deserialize_registry_response_accepts_id_or_tenant_id (covers
the {"id"|"tenant_id"} alias)
* tenants_from_env_resolution (single test covering unset →
default, csv → splits, "" → default — collapsed to one to
avoid env-var test races)
Production
- Set TENANT_REGISTRY_URL in orca-infra alongside KEYCLOAK_URL when
the registry is ready to serve. Until then, scheduler keeps using
SCHEDULER_TENANT_IDS — no operator action needed.
- Future M7.4 cleanup: once tenant-registry adoption is universal,
delete SCHEDULER_TENANT_IDS env support entirely.
Stacked on #95 (admin endpoints) since that PR added
tenant_registry_url to AgentConfig. Once #95 lands this auto-
retargets to main.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
RUSTSEC-2026-0185 (quinn-proto 0.11.14): patch-bump to 0.11.15. RUSTSEC-2026-0189 (rmcp 0.16 DNS rebinding): added to ignore with public-hostname + bearer-auth threat-model justification; rmcp 0.16->2.x migration tracked as a separate multi-hour PR.
MCP server validates per-tenant bearer tokens on incoming calls and routes each tool to the caller's tenant DB. Closes the cross-tenant data leak in the MCP path identified in M7.3.
GET /api/admin/tenants lists tenant DBs; DELETE /api/admin/tenants/{tenant_id} drops them (GDPR). Behind a separate auth path that rejects customer realm tokens.
Webhook routes live on the separate webhook server (port 3002). M7.2-C URL form is /webhook/{tenant_id}/{platform}/{repo_id}; mounting unscoped variants on the API router would mismatch handler signatures.
## Summary
- **Scan produces no results in Orca** — semgrep (`--config=auto`, unbounded memory) and syft (remote license network calls) were getting OOM-killed or hanging in resource-constrained Orca containers. Scan would "complete" with 0 findings/SBOMs silently because each scanner failure is caught and logged as a warning.
- **Dashboard Script error spam** — `document::Script` in Dioxus 0.7 needs a single text node child for inline scripts; `dangerous_inner_html` was invalid and spammed the error log on every unauthenticated page load.
## Changes
| File | Change |
|------|--------|
| `semgrep.rs` | Add `--max-memory 500 --jobs 1`; 10-minute timeout |
| `syft.rs` | Remove remote license lookup env vars; 5-minute timeout |
| `gitleaks.rs` | 5-minute timeout |
| `app_shell.rs` | Fix `dangerous_inner_html` → text child in `document::Script` |
## Test plan
- [ ] Trigger a scan on a repo in Orca — findings and SBOM entries should now appear
- [ ] Agent logs should show timeout/error warnings rather than silent empty results when tools are killed
- [ ] Navigate to dashboard unauthenticated — Script error gone from logs
- [ ] Verify scans work end-to-end with `docker compose up`
---------
Co-authored-by: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com>
Reviewed-on: #78
Each deploy job now builds the per-service image, pushes to the
private registry as :latest and :sha, then triggers an HMAC-signed
orca redeploy webhook. Coolify webhooks are no longer used.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
- Add HTTP response status checking to all Gitea tracker methods that were silently swallowing errors
- Add fallback in create_pr_review: if inline comments fail, retry as plain PR comment
## Test plan
- [ ] Deploy and trigger a PR review, check logs for actual error details
- [ ] Verify fallback posts summary comment when inline comments fail
Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com>
Co-authored-by: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com>
Reviewed-on: #47
- Remove port 143 from mailserver (only expose 993/IMAPS)
- Enable SSL_TYPE=manual with Let's Encrypt certs
- Set DOVECOT_DISABLE_PLAINTEXT_AUTH=yes
- Add pentest_imap_tls config field (defaults to true)
Fixes CERT-Bund report: IMAP PLAIN/LOGIN without TLS on 46.225.100.82:143
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add CHROME_WS_URL env var support for PDF report generation via
Chrome DevTools Protocol over WebSocket (falls back to local binary)
- Update seeded MCP server endpoint URLs on boot when MCP_ENDPOINT_URL
env var differs from stored value (previously only seeded once)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the inline <pre> JSON display with a proper browser download using
Blob + URL.createObjectURL. Clicking "Download" now saves a .json file
(CycloneDX or SPDX format) directly to the user's downloads folder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous JSON used an incorrect format. Rewrote both dashboards using
the actual SigNoz v4 schema (widgets array, builder queryData, proper
layout entries, aggregateOperator/aggregateAttribute structure).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two dashboards with ClickHouse queries matching our tracing instrumentation:
- compliance-agent: API handler latency/errors, scan pipeline stage durations,
DAST/graph/chat API panels, and warn/error log tracking
- compliance-dashboard: server function performance, page load distribution,
agent connectivity health, and error log monitoring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add repo_id, finding_id, and filter fields to tracing::instrument attributes
for better trace correlation in SigNoz. Replace all silently swallowed errors
(Err(_) => Vec::new()) with tracing::warn! logging across mod.rs, dast.rs,
graph.rs handlers. Add stage-level spans with .instrument() to pipeline
orchestrator for visibility into scan phases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fetch SSH public key every time auth section opens (was only fetching
once and caching failures)
- Add mkdir for SSH key directory in Dockerfile.agent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>