Output Safety
Overview
Output Safety is the post-execution policy layer for Cordum job results.
Input policy decides whether a job is allowed to run. Output policy decides whether its result can be released as-is, must be redacted, or must be quarantined.
Why It Exists
Input checks cannot fully predict generated output. A safe input can still produce:
- secret leakage
- sensitive PII fragments
- unsafe code or command payloads
Output Safety closes this gap before result release.
Architecture
Cordum models output checks as a two-phase flow:
- Sync metadata check on scheduler hot path.
- Optional deeper content check over dereferenced payloads.
Current scheduler wiring uses CheckOutputMeta before finalizing successful results. The scheduler persists output safety metadata for dashboard retrieval.
API Contract
Proto contract: core/protocol/proto/v1/output_policy.proto
Service:
OutputPolicyService.CheckOutput(OutputCheckRequest) -> OutputCheckResponse
Decisions:
OUTPUT_DECISION_ALLOWOUTPUT_DECISION_QUARANTINEOUTPUT_DECISION_REDACT
OutputCheckRequest includes:
- original topic, labels, tenant
- result pointer and optional inline output content
- capability/risk context (
capabilities,risk_tags) - principal and pack metadata (
principal_id,pack_id) - content metadata (
content_type,output_size_bytes,content_hash)
Policy Schema
Output rules are modeled in safety policy under output_rules.
Example:
output_rules:
- id: out-secret-1
decision: quarantine
reason: "possible cloud credential in output"
match:
topics: ["job.*"]
capabilities: ["code.write"]
risk_tags: ["secrets"]
content_patterns: ["AKIA[0-9A-Z]{16}"]
detectors: ["secret_leak"]
max_output_bytes: 1048576
Supported output decisions:
allowdenyquarantineredact
Scheduler Behavior
For succeeded jobs, scheduler output safety handling is:
- Run
CheckOutputMetawhen output safety checker is configured and enabled. - Persist
OutputSafetyRecordinto job metadata (output_safety) forGET /api/v1/jobs/{id}. QUARANTINE: move job toOUTPUT_QUARANTINEDand emit DLQ event with reason codeoutput_quarantined.REDACT: keep success state but preferredacted_ptrwhen returned.ALLOW: release result as normal.
The two phases have different failure semantics — they are not governed by a single fail mode:
- Sync metadata check (
CheckOutputMeta, hot path): fail-open on checker error or timeout. The scheduler logs the error, incrementscordum_output_policy_skipped_total, leaves the record atALLOW, and lets the job staySUCCEEDED. This phase has no configurable fail mode — it is always fail-open so a degraded checker cannot block the result pipeline (engine.go,checkOutputSafetyerror branch). - Async content scan (
CheckOutputContent, background goroutine): fail-closed by default. On checker error/timeout the output is quarantined unless the resolved tenant fail mode isopen(engine.go,startAsyncOutputCheckerror branch).
The async fail mode is configurable:
| Mode | Async behavior on checker error/timeout | When to use |
|---|---|---|
closed (default) | Quarantine the output | Production, regulated, high-risk tenants |
open | Allow result through, count as skipped | Development, low-risk tenants |
Configure the async fail mode via the OUTPUT_POLICY_FAIL_MODE environment variable at scheduler startup, or at runtime through the config service (PUT /api/v1/config with {"scheduler":{"output_fail_mode":"open"}}); runtime changes are hot-reloaded (default refresh ~30s). This is distinct from POLICY_CHECK_FAIL_MODE, which controls input policy behavior when the Safety Kernel is unreachable (see Safety Kernel). Do not confuse the two — OUTPUT_POLICY_FAIL_MODE only affects the async output-content phase; the sync metadata check is always fail-open.
A Redis-backed circuit breaker (3 failures → open for 30s → half-open probe) protects against cascading failures when the output safety checker is persistently unavailable.
Dashboard Data Shape
GET /api/v1/jobs/{id} now includes optional:
output_safety.decisionoutput_safety.reasonoutput_safety.rule_idoutput_safety.findings[]output_safety.policy_snapshotoutput_safety.redacted_ptroutput_safety.original_ptr
Metrics
Scheduler exports output safety metrics:
cordum_output_policy_checked_totalcordum_output_policy_quarantined_totalcordum_output_policy_skipped_totalcordum_output_check_latency_seconds{phase="sync|async"}
Failure Modes
- Checker unavailable/error during the sync metadata phase: always fail-open — the result stays
SUCCEEDEDandcordum_output_policy_skipped_totalis incremented (no quarantine, regardless of fail mode). - Checker unavailable/error during the async content phase: behavior depends on the resolved tenant
output_fail_mode. Default (closed) quarantines the output;openallows the result and incrementscordum_output_policy_skipped_total. - Circuit breaker open (3+ consecutive failures): all output checks are blocked until the breaker transitions to half-open after 30 seconds.
- Missing request context for result: check skipped (metadata unavailable, not a checker failure).
- Corrupt stored output safety payload: gateway/store tolerate and return empty record.
Performance Notes
- Sync check must be low-latency because it runs in result processing.
- Avoid network I/O in sync phase.
- Use deeper content scans in async/background flow where available.
Tuning False Positives
- Narrow
topicsandcapabilitiesinoutput_rules. - Set detector-appropriate confidence thresholds in checker implementations.
- Prefer targeted
content_patternsover broad regex. - Use
REDACTfor acceptable partial masking cases; reserveQUARANTINEfor high confidence/high impact findings.