astrid-runtime/astrid · error

MCP gateway control authority is incomplete

Error message

MCP gateway control authority is incomplete

What it means

A parsed GatewayRequest::Control must carry authority proof: a nonzero pid and a non-empty hook_token. read_gateway_request_inner rejects control messages missing either. This ensures only a process that legitimately holds the gateway's minted hook capability can issue stop/status operations.

Source

Thrown at crates/astrid-cli/src/commands/mcp/gateway.rs:911

            break;
        }
        line.push(byte);
    }
    if !terminated {
        anyhow::bail!("MCP attach registration is missing or too large");
    }
    let request: GatewayRequest =
        serde_json::from_slice(&line).context("MCP gateway registration is not valid JSON")?;
    match &request {
        GatewayRequest::Control(control) => {
            if control.version != GATEWAY_CONTROL_VERSION {
                anyhow::bail!(
                    "unsupported MCP gateway control version {}",
                    control.version
                );
            }
            if control.pid == 0 || control.hook_token.trim().is_empty() {
                anyhow::bail!("MCP gateway control authority is incomplete");
            }
        },
        GatewayRequest::Attach(registration) => validate_registration(registration)?,
    }
    Ok(request)
}

fn validate_registration(registration: &AttachRegistration) -> Result<()> {
    if registration.version != ATTACH_REGISTRATION_VERSION {
        anyhow::bail!(
            "unsupported MCP attach registration version {}",
            registration.version
        );
    }
    super::lifecycle::resolve_principal(Some(&registration.principal))?;
    if registration.host.trim().is_empty() {
        anyhow::bail!("MCP attach registration has an empty host");
    }

View on GitHub (pinned to affd8760f4)

Solutions

  1. Populate hook_token with the token minted at gateway startup (mint_hook_token) before sending any control request.
  2. Set pid to the real OS process id (e.g. std::process::id()) — pid 0 is rejected.
  3. If the token is unknown, restart the gateway and read the fresh token from its ready/lease files rather than guessing.
  4. Validate the control struct before sending: non-zero pid, non-blank token.

Example fix

// before
let ctl = GatewayControlRequest { version: GATEWAY_CONTROL_VERSION, ..Default::default() };
// after
let ctl = GatewayControlRequest { version: GATEWAY_CONTROL_VERSION, pid: std::process::id(), hook_token: hook_token.clone(), .. };
Defensive patterns

Strategy: validation

Validate before calling

fn control_authority_ok(pid: u32, token: &str) -> bool {
    pid != 0 && !token.trim().is_empty()
}

Type guard

fn has_authority(ctl: &GatewayControlRequest) -> bool {
    ctl.pid != 0 && !ctl.hook_token.trim().is_empty()
}

Try / catch

match result {
    Err(e) if e.to_string().contains("control authority is incomplete") => eprintln!("control request needs nonzero pid and the minted hook_token"),
    other => other?,
}

Prevention

When it happens

Trigger: Sending a control request where control.pid == 0 or control.hook_token.trim() is empty/whitespace, parsed in read_gateway_request_inner.

Common situations: Hand-crafted control payloads omitting the token; a client whose token-minting step failed or was skipped; serializing a GatewayControlRequest with default/uninitialized fields (pid defaults to 0, empty token).

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/9cbbe18cf765bbf7. Report an issue: GitHub.