Quarantine

For: Platform operators and security teams managing automatic blocking of high-risk requests.

Operations Overview | Approval Workflows | Quarantine | Risk Scoring | CP Connectivity | Telemetry

A quarantine policy decision blocks the request immediately (HTTP 403) and records a quarantine entry server-side for operator review. Unlike deny, which stops the request with no follow-up, quarantine entries persist and can be investigated, released, or used to tune policy thresholds.

Table of Contents


Quarantine vs Deny vs Approval

DecisionHTTPServer recordOperator actionUse when
deny403Execution record onlyNonePolicy violation; no review needed
quarantine403Quarantine entry + execution recordReview and optionally releaseSuspicious activity; needs investigation
require_approval202Approval recordMust approve/deny to proceedHold for human sign-off before executing

Use quarantine when you want to block and flag for investigation. The caller receives 403 (same as deny) but the entry appears in the quarantine queue for your ops team.


Triggering quarantine in Policy

Add a rule with effect.decision: quarantine in any policy layer. The risk layer is the most common location:

risk:
  - id: risk.quarantine.high_score
    priority: 100
    when:
      field: context.risk.score
      gt: "0.8"
    effect:
      decision: quarantine
      reason: "composite risk score exceeds threshold"

  - id: risk.quarantine.rate_spike
    priority: 90
    when:
      all:
        - field: context.risk.signals
          operator: in
          value: "rate_spike"
        - field: context.risk.score
          gt: "0.6"
    effect:
      decision: quarantine
      reason: "rate spike detected — workload quarantined"

You can also quarantine from other layers:

# In the targets layer: block and flag requests to disallowed targets
targets:
  - id: targets.quarantine.unknown_host
    priority: 50
    when:
      field: action.target_host
      operator: not_in
      value: "api.openai.com,api.anthropic.com"
    effect:
      decision: quarantine
      reason: "request to unregistered target — requires review"

Quarantine Entry Lifecycle

policy returns quarantine, HTTP 403 to caller

POST /release (operator action)

entry never released, persists indefinitely

entry marked released, requests re-evaluated normally

Active

Released

  • Quarantine entries do not expire automatically.
  • Releasing an entry removes the quarantine flag for that workload/request pattern; it does not automatically allow future requests. Future requests are re-evaluated against policy.
  • The entry record persists after release for audit purposes.

Reviewing Quarantine Entries

List active quarantine entries for an org:

curl -s "http://localhost:7080/v1/admin/quarantine?org_id=org-example-001&limit=20" \
  -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" | jq .

Filter by workload:

curl -s "http://localhost:7080/v1/admin/quarantine?org_id=org-example-001&workload_id=wl-prod-app-1" \
  -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" | jq .

Example entry:

{
  "quarantine_id": "01955f3e-aaaa-7abc-8def-000000000001",
  "org_id": "org-example-001",
  "workload_id": "wl-prod-app-1",
  "caller_id": "service-abc",
  "caller_ip": "10.0.1.42",
  "action_type": "ai.infer",
  "reason": "composite risk score exceeds threshold",
  "risk_score": 0.87,
  "created_at": "2026-03-12T10:15:00Z",
  "released_at": null,
  "released_by": null
}

The quarantine_id is not included in the HTTP 403 response; the entry is written server-side. Use the list endpoint to find entries for a workload.


Releasing a Quarantine Entry

To release a specific entry after investigation:

QUARANTINE_ID="01955f3e-aaaa-7abc-8def-000000000001"

curl -s -X POST "http://localhost:7080/v1/admin/quarantine/$QUARANTINE_ID/release" \
  -H "MVGC-Admin-Token: $MVGC_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"released_by": "ops-admin@example.com"}' | jq .

After release, the quarantine entry is marked with released_at and released_by. Subsequent requests from the workload are re-evaluated against policy: if the risk score has dropped below threshold, they will proceed normally.


Caller-Visible Response

The caller receives a standard 403 response. The quarantine_id is not exposed:

{
  "decision": "quarantine",
  "reason": "composite risk score exceeds threshold",
  "record_id": "01955f3e-cccc-7abc-8def-000000000003"
}

The caller cannot distinguish a quarantine 403 from a deny 403 by HTTP status alone. Use the decision field in the JSON response body to differentiate.


Admin API Reference

MethodPathDescription
GET/v1/admin/quarantineList quarantine entries. Query params: org_id, workload_id, limit
POST/v1/admin/quarantine/{id}/releaseRelease a quarantine entry. Body: {"released_by": "string"}

All admin endpoints require the MVGC-Admin-Token header.


Quarantine Entry Fields

FieldDescription
quarantine_idUUIDv7: unique quarantine entry identifier
org_idOrganisation that owns the quarantined request
workload_idWorkload that triggered quarantine
caller_idCaller identifier from the request
caller_ipSource IP of the request as observed at the gateway
action_typeAction type (e.g. ai.infer)
reasonPolicy reason code from the DSL rule's effect.reason
risk_scoreComposite risk score at the time of quarantine (if risk layer triggered it)
created_atWhen the quarantine entry was created
released_atWhen the entry was released (null if still active)
released_byOperator identity supplied on release (null if still active)

Prometheus Metrics

MetricTypeLabelsDescription
mvgc_quarantine_totalCounterorg_idTotal quarantine decisions per org
mvgc_requests_total{decision="quarantine"}Counteraction_type, org_idQuarantine decisions by action type

Alert on a sudden increase in mvgc_quarantine_total to detect workloads behaving anomalously.


Troubleshooting

SymptomLikely causeFix
Quarantine entry not appearing in listWrong org_id filterCheck org_id in the request body that triggered the quarantine
Workload still quarantined after releaseRisk score still above threshold; new requests re-evaluatedInvestigate the underlying risk signals; adjust MVGC_RISK_* thresholds if needed
Caller gets 403 but no quarantine entryRule uses deny not quarantineCheck the policy rule's effect.decision field
High quarantine volume from one workloadPolicy threshold too low for normal trafficReview MVGC_RISK_RATE_THRESHOLD and MVGC_RISK_COST_THRESHOLD values

See Also