Skip to main content
sec0-sdk/guard is the smallest Sec0 integration surface. Use it when you want policy decisions in application code without wrapping a tool server. Typical uses:
  • outbound message checks before sending to Slack, Discord, email, or SMS
  • preflight checks around external API calls
  • tool or MCP decisions in apps that do not use sec0-sdk/middleware
  • approval workflows that need a simple create, wait, and resolve loop

Core API

createSec0Guard(...) returns three methods:
MethodUse it for
guard.check(input)get a decision without executing anything
guard.execute(input, actionFn, handlers?)run the action only when the decision allows it, redacts it, or resolves escalation
guard.waitForResolution(escalationId, opts?)wait on an existing escalation
Decision outcomes are:
OutcomeMeaning
allowproceed as-is
redactexecute with redacted content
blockstop immediately
escalatecreate an escalation and either wait or return pending

Choose a Mode

ModePolicy source
standalonelocal GuardPolicy, local policy.yaml, or local PolicyObject
dashboardremote control-plane policy only
hybridlocal plus remote, with remote-first or local-first precedence

Standalone Example

Use standalone mode when the application must work without network access:
import { createSec0Guard } from "sec0-sdk/guard";

const guard = createSec0Guard({
  mode: "standalone",
  provider: {
    local: {
      policy: {
        defaultOutcome: "allow",
        rules: [
          {
            kind: "message_outbound",
            target: "discord:supplier",
            outcome: "block",
            reason: "supplier_messages_require_review",
          },
        ],
      },
    },
  },
});

await guard.execute(
  {
    kind: "message_outbound",
    target: "discord:supplier",
    content: outboundMessage,
  },
  async () => sendToSupplier(outboundMessage),
);

Dashboard Example

Use dashboard mode when policy should come from the control plane:
import { createSec0Guard } from "sec0-sdk/guard";

const guard = createSec0Guard({
  mode: "dashboard",
  provider: {
    remote: {
      auth: { apiKey: process.env.SEC0_API_KEY! },
      source: {
        source: "control-plane",
        level: "middleware",
        scope: "base",
        refreshTtlMs: 60_000,
      },
    },
  },
});

const decision = await guard.check({
  kind: "tool_call",
  target: "payments:refund@1.0",
  content: { chargeId: "ch_123" },
});

Hybrid Example

Use hybrid mode when the remote policy should be primary but local fallback must still work:
import { createSec0Guard } from "sec0-sdk/guard";

const guard = createSec0Guard({
  mode: "hybrid",
  provider: {
    precedence: "remote-first",
    remote: {
      auth: { apiKey: process.env.SEC0_API_KEY! },
      source: { source: "control-plane", level: "middleware", scope: "base" },
    },
    local: {
      policyPath: "./policy.fallback.yaml",
      cacheTtlMs: 5 * 60_000,
    },
  },
});
If the remote policy fails after a prior successful fetch, the guard can continue from its remote cache. If no cache exists, hybrid mode falls back to the local provider.

Execute with Redaction or Block Handlers

guard.execute(...) lets you customize what happens on redact and block outcomes:
const result = await guard.execute(
  {
    kind: "api_call",
    target: "https://billing.example.com/refunds",
    content: payload,
  },
  async (input) => billingClient.refund(input.content),
  {
    onRedactInput(input, decision) {
      return { ...input, content: decision.redactedContent };
    },
    onBlock(decision) {
      return { blocked: true, reason: decision.reason };
    },
  },
);

Escalations and Approvals Transport

The guard can create and wait on escalations, and it can forward those escalation events to the open-source approvals bridge.
import {
  createApprovalsBridgeTransport,
  createSec0Guard,
} from "sec0-sdk/guard";

const guard = createSec0Guard({
  mode: "dashboard",
  provider: {
    remote: {
      auth: { apiKey: process.env.SEC0_API_KEY! },
      source: { source: "control-plane", level: "middleware", scope: "base" },
    },
  },
  escalation: {
    waitForResolutionByDefault: true,
    timeoutMs: 5 * 60_000,
    pollIntervalMs: 2000,
  },
  transport: createApprovalsBridgeTransport({
    bridgeUrl: process.env.SEC0_APPROVALS_BRIDGE_URL!,
    tenantId: process.env.SEC0_TENANT_ID!,
    bearerToken: process.env.SEC0_APPROVALS_BRIDGE_TOKEN,
    sharedSecret: process.env.SEC0_APPROVALS_BRIDGE_SHARED_SECRET,
  }),
});
The transport emits escalation.created and escalation.finalized events to the bridge. Deploy the reference worker in apps/sec0-approvals-bridge when you want Discord or Telegram approval flows. If your application already knows when to escalate and does not need policy evaluation, use createEscalationManager(...) from sec0-sdk/escalation instead of sec0-sdk/guard. Guard now builds on that same shared escalation lifecycle.

Waiting on an Existing Escalation

If your application stores the escalation id and resolves it later:
const resolution = await guard.waitForResolution("esc_123", {
  timeoutMs: 120_000,
  pollIntervalMs: 2000,
});
The final status is one of approved, rejected, expired, or timeout. The equivalent standalone API is:
import { createEscalationManager } from "sec0-sdk/escalation";

const escalation = createEscalationManager({
  apiKey: process.env.SEC0_API_KEY!,
  controlPlaneUrl: process.env.SEC0_CONTROL_PLANE_URL,
});

const resolution = await escalation.waitForResolution("esc_123", {
  timeoutMs: 120_000,
  pollIntervalMs: 2000,
});

When to Use Guard vs Middleware

Use sec0-sdk/guard when the action lives in application code. Use sec0-sdk/middleware when the action is a tool-server invocation and you want registry freeze, source hashing, signed envelopes, and server-side policy enforcement on every call.