Skip to content

Detectord: fix stale setup when installing release over demo - #68

Merged
dimitriosGX merged 2 commits into
mainfrom
dk-detectord
Aug 21, 2026
Merged

Detectord: fix stale setup when installing release over demo#68
dimitriosGX merged 2 commits into
mainfrom
dk-detectord

Conversation

@dimitriosGX

@dimitriosGX dimitriosGX commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary by cubic

Fixes stale backend state when switching from demo to release by rebuilding the backend client on credential changes and refusing to cache a policy from the wrong tenant. Previously the daemon kept polling the old backend and persisted that tenant’s policy; now it verifies org_id, warns, and keeps the last-known-good policy on mismatch.

  • mcp_backend::Policy includes optional org_id parsed from domain-config; empty/absent is treated as None for backward compatibility.
  • mcp_detector_daemon rebuilds the BackendClient when api_base_url or api_key changes and forces an immediate refresh.
  • Centralized apply_policy checks the tenant (org_id) before persisting; on mismatch it warns and keeps the cache. Older backends without org_id remain accepted.
  • refresh_policy logs fetch failures and routes successful updates through apply_policy, preserving the cached policy on error.

Written for commit a76a9af. Summary will update on new commits.

Review in cubic

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/detectord/crates/mcp_detector_daemon/src/runner.rs">

<violation number="1" location="crates/detectord/crates/mcp_detector_daemon/src/runner.rs:134">
P1: When a running worker is re-enrolled into a different org, this block updates `enrollment` but leaves `seen` bound to the original org. Reopen `SeenStore` with `fresh.org_id` before assigning `enrollment`; otherwise release fingerprints are keyed as demo data, and a restart loses them.</violation>
</file>

<file name="crates/detectord/crates/mcp_detector_daemon/src/ops.rs">

<violation number="1" location="crates/detectord/crates/mcp_detector_daemon/src/ops.rs:396">
P2: When the enrollment file cannot be written, `apply_policy` discards `save_for`'s error, so this refresh returns `Ok(Status)` after `status()` reloads the old policy. Make policy application fallible and propagate the persistence error here, as the previous direct save did.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +134 to +141
if fresh.api_base_url != enrollment.api_base_url || fresh.api_key != enrollment.api_key
{
tracing::info!(url = %fresh.api_base_url, "backend credentials changed; rebuilding client");
client = BackendClient::new(fresh.api_base_url.clone(), fresh.api_key.clone());
// Don't sit on the wrong tenant's cached policy for up to
// REFRESH_INTERVAL; re-fetch on this pass.
force_refresh = true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: When a running worker is re-enrolled into a different org, this block updates enrollment but leaves seen bound to the original org. Reopen SeenStore with fresh.org_id before assigning enrollment; otherwise release fingerprints are keyed as demo data, and a restart loses them.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/detectord/crates/mcp_detector_daemon/src/runner.rs, line 134:

<comment>When a running worker is re-enrolled into a different org, this block updates `enrollment` but leaves `seen` bound to the original org. Reopen `SeenStore` with `fresh.org_id` before assigning `enrollment`; otherwise release fingerprints are keyed as demo data, and a restart loses them.</comment>

<file context>
@@ -123,6 +125,20 @@ pub async fn worker(user: String, enforce: bool, events: Option<EventTx>) -> any
+            // the worker keeps polling the OLD backend for the whole life of
+            // the process: policy and fingerprints come back for the wrong
+            // tenant, and `refresh` persists that answer over the correct one.
+            if fresh.api_base_url != enrollment.api_base_url || fresh.api_key != enrollment.api_key
+            {
+                tracing::info!(url = %fresh.api_base_url, "backend credentials changed; rebuilding client");
</file context>
Suggested change
if fresh.api_base_url != enrollment.api_base_url || fresh.api_key != enrollment.api_key
{
tracing::info!(url = %fresh.api_base_url, "backend credentials changed; rebuilding client");
client = BackendClient::new(fresh.api_base_url.clone(), fresh.api_key.clone());
// Don't sit on the wrong tenant's cached policy for up to
// REFRESH_INTERVAL; re-fetch on this pass.
force_refresh = true;
}
if fresh.api_base_url != enrollment.api_base_url || fresh.api_key != enrollment.api_key
{
tracing::info!(url = %fresh.api_base_url, "backend credentials changed; rebuilding client");
client = BackendClient::new(fresh.api_base_url.clone(), fresh.api_key.clone());
// Don't sit on the wrong tenant's cached policy for up to
// REFRESH_INTERVAL; re-fetch on this pass.
force_refresh = true;
}
if fresh.org_id != enrollment.org_id {
seen = SeenStore::open(paths::seen_store_path(&user), fresh.org_id.clone())?;
}

match client.fetch_policy().await {
// Same tenant check the reconcile loop applies; `apply_policy`
// persists on its own.
Ok(p) => crate::runner::apply_policy(&p, &mut e, user),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When the enrollment file cannot be written, apply_policy discards save_for's error, so this refresh returns Ok(Status) after status() reloads the old policy. Make policy application fallible and propagate the persistence error here, as the previous direct save did.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/detectord/crates/mcp_detector_daemon/src/ops.rs, line 396:

<comment>When the enrollment file cannot be written, `apply_policy` discards `save_for`'s error, so this refresh returns `Ok(Status)` after `status()` reloads the old policy. Make policy application fallible and propagate the persistence error here, as the previous direct save did.</comment>

<file context>
@@ -382,12 +382,21 @@ pub fn status(user: &str) -> anyhow::Result<Status> {
+        match client.fetch_policy().await {
+            // Same tenant check the reconcile loop applies; `apply_policy`
+            // persists on its own.
+            Ok(p) => crate::runner::apply_policy(&p, &mut e, user),
+            Err(err) => {
+                tracing::warn!(error = %err, "policy refresh failed; keeping last-known-good")
</file context>

@dimitriosGX
dimitriosGX merged commit cddbfa8 into main Aug 21, 2026
12 checks passed
@dimitriosGX
dimitriosGX deleted the dk-detectord branch August 21, 2026 10:44
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