Skip to main content
sec0-sdk/gateway centralizes authentication, authorization, entitlements, quotas, vendor token brokering, idempotency/dedupe, and audit at network boundaries. Use it when tool execution crosses process or network boundaries so custody and policy enforcement continue across hops.

When to Use the Gateway

  • Your agents call tool servers over HTTP/HTTPS
  • You need centralized OIDC auth at the network edge
  • You want per-tenant/per-user rate limits and daily quotas
  • You need vendor API key injection without exposing keys to agents
  • You want idempotency/dedupe for side-effecting tool calls
  • You need egress and filesystem allowlists at the boundary

Starting the Gateway

import { startGatewayServer, InMemoryAdapter } from "sec0-sdk/gateway";
import { Sec0Appender } from "sec0-sdk/audit";
import { LocalDevSigner } from "sec0-sdk/signer";

const signer = LocalDevSigner.fromKeyRef("file://./.sec0/keys/ed25519.key");
const appender = new Sec0Appender({ config: { dir: ".sec0" }, signer });

startGatewayServer({
  port: 8088,
  tenant: "my-app",

  // Map server names to upstream base URLs
  targets: {
    "vision-mcp": "https://vision.example.com",
    "database-mcp": "https://db.example.com",
    "stripe-mcp": "https://stripe-adapter.example.com",
  },

  // Required: audit sink
  audit: {
    append: (env) => appender.append(env),
    gatewayName: "sec0.gateway",
    gatewayVersion: "1.0.0",
  },

  // Required: quota adapter
  quotas: { adapter: new InMemoryAdapter() },

  // Start in observe mode, then switch to enforce
  enforcement: { mode: "observe" },
});

Gateway Endpoints

EndpointDescription
POST /mcp/:server/:toolAtVersionInvoke a tool on an upstream server
POST /a2a/invokeAgent-to-agent convenience envelope

Required Headers

HeaderPurpose
x-idempotency-keyRequired when dedupe or side-effect idempotency policies are enabled
x-node-idAgent state: node identifier
x-agent-refAgent state: run reference
x-agent-stateAgent state: full encoded payload

Authentication

OIDC JWT Validation

startGatewayServer({
  // ...
  authn: {
    allowUnauthenticated: false,
    oidc: {
      issuer: "https://auth.example.com",
      audience: "sec0.gateway",
      jwks_uri: "https://auth.example.com/.well-known/jwks.json",
      tenantClaim: "org",
      userClaim: "email",
      rolesClaim: "roles",
      subjectClaim: "sub",
      clientIdClaim: "azp",
    },
  },
});
For machine-to-machine agents, use OIDC client-credentials tokens (the gateway can authorize these via agents entitlements using the token azp/client_id claim).

Entitlements

Control which tenants, users, roles, and service agents can access which tools:
startGatewayServer({
  // ...
  entitlements: {
    default_plan: "free",
    plans: {
      free: { calls_per_day: 1000 },
      pro: { calls_per_day: 100000 },
    },
    tenants: {
      "acme-corp": {
        plan: "pro",
        allowlist: [],
        denylist: ["mcp://payments/refund@2.0"],
        per_tool: {
          "mcp://fetch/fetch.get@1.0": { rate_per_sec: 10, calls_per_day: 50000 },
        },
        users: {
          "admin@acme.com": { allowlist: ["*"] },
          "readonly@acme.com": { allowlist: ["mcp://fetch/*@1.*"] },
        },
        user_ids: {
          "auth0|usr_123": { allowlist: ["mcp://ehr/patient.read@r4"] },
        },
        roles: {
          "security-analyst": { allowlist: ["mcp://posture/*@1.*"] },
          "contractor": { denylist: ["mcp://payments/*@2.*"] },
        },
        agents: {
          "sec0-orchestrator-prod": { allowlist: ["mcp://payments/charge@2.*"] },
        },
        agent_nodes: {
          "workflow-planner": { allowlist: ["mcp://planner/*@1.*"] },
        },
      },
    },
  },
});

Quotas

Rate limiting and daily limits via pluggable adapters:
import { InMemoryAdapter } from "sec0-sdk/gateway";

startGatewayServer({
  // ...
  quotas: {
    adapter: new InMemoryAdapter(),
    // Or use a Redis adapter for production:
    // adapter: new RedisAdapter({ url: process.env.REDIS_URL }),
  },
});

QuotaAdapter Interface

interface QuotaAdapter {
  incrDaily(tenant: string, tool: string, limit: number, now?: Date):
    Promise<{ count: number; resetAt: Date }>;
  takeToken(tenant: string, tool: string, ratePerSec: number, burst?: number, now?: Date):
    Promise<{ tokens: number; resetAt: Date }>;
}

Vendor Token Brokering

Inject upstream API keys without exposing them to agents:
startGatewayServer({
  // ...
  brokers: {
    "stripe-mcp": {
      type: "vendor_key_pool",
      key_refs: [
        "file:///etc/sec0/stripe-key-1.txt",
        "file:///etc/sec0/stripe-key-2.txt",
      ],
      inject: {
        header: "Authorization",
        format: "bearer",
      },
    },
  },
});

Broker Types

TypeDescription
vendor_key_poolRound-robin from a pool of API keys
oidc_on_behalfExchange tokens via OIDC on-behalf-of flow
staticInject a single static key

Boundary Guardrails

Restrict what upstream servers can access:
startGatewayServer({
  // ...
  security: {
    egress_allowlist: ["*.example.com", "api.stripe.com"],
    fs_allowlist: ["/tmp/sec0-*"],
    limits: {
      max_payload_kb: 1024,
      max_duration_ms: 30000,
    },
    side_effects: {
      require_idempotency_key: true,
      approve_high_risk: true,
    },
    allow_versions: {
      "vision-mcp": ["1.0.0", "1.1.0"],
      "database-mcp": ["2.*"],
    },
  },
});

Idempotency & Dedupe

Prevent duplicate side-effect calls:
startGatewayServer({
  // ...
  dedupe: {
    enabled: true,
    duplicatePolicy: "replay",  // "replay" | "reject"
    ttlSec: 3600,               // Cache window
    namespace: "sec0-gw",       // Key prefix
    redisUrl: process.env.REDIS_URL,  // Optional Redis backend
  },
});

AP2 Mandate Enforcement

Verify multi-hop intent and cart mandates:
startGatewayServer({
  // ...
  ap2: {
    enabled: true,
    requireForSideEffects: true,
    headers: {
      intent: "x-ap2-intent-mandate",
      cart: "x-ap2-cart-mandate",
      bundle: "x-ap2-bundle",
    },
    trust: {
      didMethods: ["did:web"],
      issuersAllowlist: ["did:web:example.com"],
      clockSkewSec: 60,
    },
    mode: "full",  // "" | "observe" | "partial" | "full"
  },
});

Agent State Analytics

Opt-in to derived telemetry written into agent state:
startGatewayServer({
  // ...
  agentState: {
    enableGatewayAnalytics: true,
    includeAp2: true,
    includeRisk: true,
  },
});

Remote Runtime Decisioning

Use the gateway’s runtime adapter when final allow or deny decisions should be delegated to a remote service:
startGatewayServer({
  // ...
  runtime: {
    enforcement: {
      mode: "remote",
      protocolVersion: "2026-02-01",
      failureMode: "local",
      remote: {
        endpoint: process.env.SEC0_RUNTIME_URL!,
        apiKey: process.env.SEC0_RUNTIME_API_KEY,
        timeoutMs: 3000,
      },
    },
  },
});

Using the Pipeline Directly

If you already have an HTTP server, use the lower-level pipeline:
import { gatewayPipeline } from "sec0-sdk/gateway";

// In your route handler:
await gatewayPipeline(config, serverName, toolAtVersion, ctx, span, next);

Policy-Driven Configuration

Map a Sec0 policy YAML into gateway config:
startGatewayServer({
  port: 8088,
  tenant: "my-app",
  targets: { /* ... */ },
  policyYaml: fs.readFileSync("./policy.yaml", "utf8"),
  // ... audit and quotas still required
});
For the full gateway configuration reference, see Gateway Config Reference.