Skip to main content

Edge Policy & Modes

Edge reuses the Safety Kernel policy evaluator for coding-agent actions. Raw Claude Code hook input is normalized into deterministic policy inputs before evaluation:

  • Topic: job.edge.action (a Safety Kernel compatibility namespace — not a Cordum Job topic, queue, or worker-dispatch contract).
  • Capability: classifier-owned category such as exec.shell, file.read, file.write, or edge.unknown.
  • Risk tags: test, build, secrets, destructive, write, git, network, unknown, …
  • Labels: bounded values such as hook.tool_name, command.class, command.family, path.class, unknown.impact.

The Gateway/classifier owns raw parsing and redaction; the Safety Kernel sees normalized metadata and bounded redacted input only — never raw nested fields like tool_input.command.

Decisions

POST /api/v1/edge/evaluate returns one of:

DecisionMeaning
ALLOWAction may proceed.
DENYAction is blocked; the agent sees the deny reason.
REQUIRE_APPROVALA human must approve before the action runs (see below).
THROTTLEAction is rate-limited / deferred.
CONSTRAINAction may proceed with modified/constrained input.

Classifier mapping (demo policy)

The bundled examples/cordum-edge-pack demonstrates typical rules:

ActionCapabilityKey labelsDemo behavior
npm test, go test, pytest, vitestexec.shellcommand.family=testAllow (allow-safe-build-test).
npm run build, go build, make buildexec.shellcommand.family=buildAllow (allow-safe-build-test).
rm -rf and similar recursive deletesexec.shellcommand.family=filesystem_deleteDeny (deny-destructive-shell).
Read of .env, keys, tokens, credentialsfile.readpath.class=secretDeny (deny-secret-reads).
Edit/Write/MultiEdit source filefile.writepath.class=source_codeRequire approval (require-approval-for-edits).
git push …exec.shellcommand.family=git_pushRequire approval (require-approval-for-vcs-push).
curl/wget/ssh/nc egressexec.shellcommand.family=network_egressRequire approval (require-approval-for-network).
Unknown high-impact actionedge.unknownunknown.impact=highDeny (deny-unknown-high-risk).

A narrower policy.production.fragment.yaml keeps deny-by-default for secrets, destructive shell, and unknown high-risk actions while allowing safe local tests/builds under explicit constraints. Neither fragment is a complete enterprise enforcement boundary on its own.

Policy modes

ModeUseBehavior when Cordum governance is unavailable
observeDiscovery and low-friction dev visibility.Allow degraded actions, record evidence where possible.
enforceLocal enforcement for risky/unknown actions.Allow known-safe actions only; deny risky/unclassified actions.
enterprise-strictManaged enterprise rollout.Fail closed.
requires-edge-governance (tag)Production workflow action that must be governed.Fail closed on Gateway miss regardless of session mode.

Mode is set via --policy-mode or CORDUM_EDGE_POLICY_MODE, which accept only observe, enforce, or enterprise-strict; see Configuration. local-dev-enforce is a hook-recognized CORDUM_EDGE_MODE value for fail-closed enforcement (e.g. set via a settings file); it is not settable through --policy-mode / CORDUM_EDGE_POLICY_MODE, and the standard cordumctl edge claude launcher emits the selected policy-mode value (observe/enforce/enterprise-strict) as CORDUM_EDGE_MODE, not local-dev-enforce.

URL egress DNS/SSRF gate

The URL action gate guards governed URL actions before they reach an approval or allow decision. For non-literal hostnames it resolves the host and denies if any answer is loopback, RFC1918/private, link-local, IPv6 ULA, unspecified, multicast, or a known cloud-metadata literal. Known exfiltration hosts, paste destinations, prompt-exfil query signatures, and literal metadata/private IPs are denied before DNS. Resolver uncertainty (errors, empty answer sets, malformed addresses) fails closed.

Approval retry

The default UX is an immediate REQUIRE_APPROVAL plus retry coordinates; an opt-in inline wait is available for local/demo callers only.

Default flow: deny + approve-then-retry

evaluate returns decision=REQUIRE_APPROVAL with everything the caller needs to retry once a human approves:

FieldPurpose
approval_refServer-generated ID (edge_appr_…) passed back on retry to consume the approval.
approval_urlDashboard path /edge/approvals/<approval_ref> for reviewers.
action_hashsha256:<hex> over the canonical action. Server-derived; client hashes are not trusted.
input_hashsha256:<hex> over the redacted input.
policy_snapshotSafety Kernel snapshot the approval is bound to.
wait_strategy / wait_aftermanual_approval / approve_then_retry.

Repeated evaluates of the same action reuse the same pending approval (bound to the tenant/session/execution/action_hash/policy_snapshot tuple) rather than spamming new ones.

Retry: consume-once

The caller re-issues evaluate with the same body plus approval_ref. The Gateway recomputes action_hash against the fresh policy snapshot, then:

  • approved + matching hash + matching snapshot → ALLOW once (the store atomically marks consumed_at under WATCH/MULTI).
  • mismatched hash/snapshot → DENY; the approval is not consumed.
  • already consumed → DENY "approval already consumed".
  • rejectedDENY echoing the resolution reason.
  • expired/invalidatedDENY "approval expired".
  • pendingREQUIRE_APPROVAL (still waiting).

A consumed approval is single-use; there is no "approve a class of actions" or "approve for the next 5 minutes". Approvals never create Cordum Jobs and never bypass tenant isolation.

ProvenanceGate: resolved approval evidence

For destructive action-gate decisions, a backend approved approval is necessary but not sufficient. After the mutation gate validates the stored approval, ProvenanceGate verifies the tenant audit chain and requires a canonical resolved approval event:

  • event type edge.approval_resolved, decision approved/approve;
  • exact tenant, approval_ref, and action_hash match in bounded audit extra.

A requested-only (edge.approval_requested) row, wrong tenant/ref/hash, rejected/expired decision, malformed event, or verifier outage is an evidence gap and fails closed — with bounded reason codes such as audit_evidence_missing, audit_chain_compromised, or audit_chain_verifier_unavailable. Raw prompts, tool payloads, transcripts, and command output are never embedded in audit evidence.

Optional inline wait (demo only)

evaluate accepts wait_for_approval: true and approval_wait_timeout_ms (server-clamped to 5 minutes, default 30s) to block on the approval and route through the same consume-once path. The standalone POST /api/v1/edge/approvals/{approval_ref}/wait endpoint is observation-only: it returns the resolved (or still-pending) approval and never consumes one. Production hooks and agentd should default to wait_for_approval: false.