execution authorization protocol

Control what AI agents can execute.

Deterministic evaluation: (intent, state, policy) → ALLOW | DENY. OxDeAI controls execution, not behavior.

No valid authorization → no execution through the enforced guard boundary.

Agents propose actions. OxDeAI decides if they are allowed to execute. Without this boundary, there is no enforced control over what agents can do.

Deterministic decisions Fail-closed boundary Signed authorization artifacts

Prompts and monitors are not enforcement.

Agents can call APIs, provision infrastructure, move money. You need a boundary that holds before any side effect occurs.

Prompt guardrails
Shape behavior. Don't enforce execution.
Guardrails influence what an agent is likely to do. They are probabilistic. They can be bypassed, jailbroken, or drift with model versions. They do not prevent execution.
Monitoring / logging
Tells you what happened. After the fact.
Observability is essential. It is not a control plane. By the time you see the log, the API was called, the transaction was sent, the resource was provisioned.
OxDeAI
Fail-closed boundary. Before side effects.
Every action requires a valid, signed, policy-bound authorization artifact before execution is permitted. No authorization → no execution. Deterministic. Verifiable. Auditable.

The boundary holds in every case.

Four outcomes. No path to execution without a valid authorization artifact.

ALLOW Policy passes. Signed artifact issued. Execution proceeds.
DENY Policy blocks. No artifact issued. No execution. No side effect.
REPLAY auth_id already consumed. Execution blocked. Fail-closed.
BYPASS Direct upstream call without authorization → 403 rejected.
non-bypassable-demo · oxdeai
OxDeAI non-bypassable terminal demo: direct call returns 403, gateway path enforces ALLOW, DENY, and REPLAY

No valid authorization → no execution path.

$ export UPSTREAM_EXECUTOR_TOKEN=demo-internal-token
$ node examples/non-bypassable-demo/protected-upstream.mjs
$ node examples/non-bypassable-demo/pep-gateway.mjs
$ node examples/non-bypassable-demo/agent.mjs
View demo source →

Decision and execution are separated.

The Policy Decision Point (PDP) decides. The Policy Enforcement Point (PEP) enforces. The Verifier checks trust. Execution cannot proceed through the guard without a valid authorization.

01
Agent proposes action
Agent produces a proposed action:
name, args, estimated cost,
agent ID, target, metadata.
02
PEP normalizes → PDP evaluates
Guard normalizes to Intent.
(intent, state, policy) →
deterministic evaluation.
03a
ALLOW
Signed AuthorizationV1 artifact issued.
Verifier checks trustedKeySets.
auth_id consumed (replay-safe).
03b
DENY
Blocked before execution.
No side effect occurs.
No artifact issued.
A valid signature is not trust. trustedKeySets must be configured explicitly by the verifier. In strict mode, missing key sets → TRUSTED_KEYSETS_REQUIRED. A valid signature from an unknown issuer still fails closed.

What the implementation actually guarantees.

Properties derived directly from the protocol spec and conformance tests.

✓
Deterministic decisions
Policy evaluation is deterministic for the same normalized intent, trusted state, policy, and trusted evaluation time. The reference engine has no ambient randomness or shared mutable state, so restarts and replicas of it produce identical decisions.
✓
Replay protection
Authorization IDs are single-use via a pluggable ReplayStore. Nonce window tracking per agent. Redis-backed replay protection is available for durable deployments. TOCTOU tests are part of the conformance suite.
✓
Explicit trust model
trustedKeySets must be configured by the verifier. In strict mode, missing key sets → TRUSTED_KEYSETS_REQUIRED. A valid signature from an unknown issuer still fails closed.
✓
Local, offline verification
Authorization artifacts are self-contained. Verification requires no network calls, no central authority, no runtime dependency on the issuer. Authorization artifacts can be verified fully offline by the TypeScript reference implementation.
✓
Verifiable audit trail
Hash-chained audit log built into the engine. Decisions are serialized to a verification envelope (VerificationEnvelopeV1 draft) with state snapshots and event sequences. Envelope signatures are optional: an envelope is authenticated only when the verifier sets requireSignatureVerification with trustedKeySets. Without it, verification proves structure, not origin. By contrast, the guard always verifies AuthorizationV1 signatures in strict mode before execution.
✓
Cross-language verification
Canonicalization vectors are cross-validated in TypeScript, Go, and Python from one shared corpus; state-hash and signed-KRL vectors are cross-validated in Go and Python. The broader protocol conformance suite is validated in TypeScript. All supported adapters (LangGraph, CrewAI, AutoGen, OpenAI Agents, OpenClaw) delegate authorization to @oxdeai/guard and are exercised in CI.

Put the guard on every execution path.

Trusted execution context is established at the boundary. Actions execute only after authorization succeeds.

Examples track main. The guard setup and strict verification examples run on the published @oxdeai/core@2.0.0 and @oxdeai/guard@2.0.0. Guard-enforced delegation recipient binding is on main only and is not in @oxdeai/guard@2.0.0; the delegation example shows the explicit check 2.0.0 requires.
TypeScript
import { PolicyEngine, RECOMMENDED_TRUSTED_TIME_PROFILE, type Intent } from "@oxdeai/core";
import { createSecureGuard, createTrustedExecutionContext, defaultNormalizeAction, type ProposedAction,
         OxDeAINormalizationError, OxDeAIProvenanceConflictError, OxDeAIDenyError } from "@oxdeai/guard";

// 1. Initialize the Policy Decision Point (PDP)
const engine = new PolicyEngine({
  policy_version:             "v1.0.0",
  engine_secret:              process.env.OXDEAI_ENGINE_SECRET!,      // set at deploy time
  authorization_ttl_seconds:  60,
  authorization_signing_alg:  "Ed25519",
  authorization_signing_kid:  "k1",
  authorization_private_key_pem: process.env.OXDEAI_SIGNING_KEY_PEM!, // set at deploy time
  authorization_audience:     "agent-001",
  ...RECOMMENDED_TRUSTED_TIME_PROFILE,   // required trusted-time bounds
});

// 2. Map the action explicitly: the amount and type the policy evaluates are
//    read from the same args the callback executes. Nothing is inferred or
//    defaulted. Amounts are fixed-point with 6 decimals: 1 USD = 1_000_000n.
function toIntent(action: ProposedAction): Intent {
  const amount = action.args["amount_micros"];
  if (action.name !== "charge_wallet" || typeof amount !== "string" || !/^[1-9][0-9]*$/.test(amount)) {
    throw new OxDeAINormalizationError("charge_wallet requires a positive integer amount_micros");
  }
  return { ...defaultNormalizeAction(action), action_type: "PAYMENT", amount: BigInt(amount) };
}

// 3. Create the secure guard. Tenancy posture is explicit, never assumed.
const guard = createSecureGuard(
  {
    engine,
    getState:        () => getAgentState(),        // returns { state, version }
    setState:        (s, v) => setAgentState(s, v),  // CAS: returns boolean
    trustedKeySets:   [myKeySet],
    expectedAudience: "agent-001",  // must match authorization_audience above
    mapActionToIntent: toIntent,     // explicit amount and type, no estimatedCost default of 0
    onDecision:      (record) => auditLog(record),
    onBoundaryEvent: (event) => auditBoundary(event),  // identity/tool conflicts, CAS conflicts
  },
  { tenancy: "single-tenant" }
);

// 4. The PEP builds trusted context AFTER authenticating the caller,
//    never by trusting a field on the incoming request body.
const trustedContext = createTrustedExecutionContext({
  principalId: authenticatedPrincipalId,
  agentId:     resolveAgentId(authenticatedPrincipalId),
  adapterId:   "http-pep",
  depth:       0,   // derived from the real call chain, never proposer-supplied
});

// 5. Validate once, then authorize and execute the same frozen value.
//    The action carries no identity claim: identity came from step 4.
const charge = Object.freeze({ wallet_id: walletId, amount_micros: "100000000", currency: "usd" });  // 100.00 USD
try {
  await guard(
    trustedContext,
    { name: "charge_wallet", args: charge },
    async () => chargeWallet(charge)   // only runs if ALLOW, with the authorized amount
  );
} catch (err) {
  if (err instanceof OxDeAIProvenanceConflictError) {
    console.error("Trusted-context conflict");
  } else if (err instanceof OxDeAIDenyError) {
    console.error("Action denied");
  } else {
    throw err;
  }
}
import { verifyAuthorization, createVerifier, verifyEnvelope } from "@oxdeai/core";

// Authorization: strict mode requires trustedKeySets and a verified Ed25519 signature
const result = verifyAuthorization(artifact, {
  mode:             "strict",
  trustedKeySets:   [myKeySet],
  expectedPolicyId: "policy-production-v1",
});
if (result.status !== "ok" || !result.signatureVerified) {
  throw new Error("Authorization not verified");   // fail closed
}
// A valid signature from an unknown issuer → still fails closed
// Missing trustedKeySets in strict mode → TRUSTED_KEYSETS_REQUIRED

// Bound verifier: pre-configure trust once, reuse everywhere
const verifier = createVerifier({
  trustedKeySets: [myKeySet],     // required, non-empty
  expectedIssuer: "oxdeai-pdp",
});

// Envelope: structural checks (snapshot, hash-chained audit, policy id) always run.
// Envelope signatures are optional: strict mode with trustedKeySets alone still
// accepts an UNSIGNED envelope. requireSignatureVerification makes it mandatory.
const envelopeResult = verifyEnvelope(envelopeBytes, {
  mode:                         "strict",
  trustedKeySets:               [myKeySet],
  expectedIssuer:               "oxdeai-pdp",
  requireSignatureVerification: true,   // unsigned → ENVELOPE_SIGNATURE_MISSING
  expectedPolicyId:             "policy-production-v1",
});

if (envelopeResult.status !== "ok") {
  throw new Error("Audit envelope not verified");   // fail closed
}
// With requireSignatureVerification, "ok" means the audit trail is structurally
// intact AND signed by a trusted key for expectedIssuer. Without it, "ok" proves
// structure only.
import { createDelegation } from "@oxdeai/core";
import { createSecureGuard, createTrustedExecutionContext } from "@oxdeai/guard";

// Parent agent ("agent-001", the audience of parentAuth) narrows its authority
// for exactly one child agent. Amounts are fixed-point: 1 USD = 1_000_000n.
const delegation = createDelegation(
  parentAuth,
  {
    delegatee: "child-agent-002",   // the only agent this artifact is issued to
    scope: {
      tools:      ["provision_gpu"],
      max_amount: 300_000_000n,     // 300 USD, strictly narrowed from parent
    },
    expiry: parentAuth.expiry,   // cannot exceed parent
    kid:    "agent-001-k1",     // signed by the parent agent (issuer "agent-001")
  },
  parentAgentKeyPem   // private key passed separately, never inside params
);

// Child-side PEP. DelegationV1 is a signed grant; it does not prove who presents it.
const guard = createSecureGuard(
  {
    engine, getState, setState,
    trustedKeySets:   [pdpKeySet, parentAgentKeySet],   // parentAuth and delegation signers
    expectedAudience: "agent-001",   // parentAuth.audience, the delegator
    trustedDelegationAuthorities: [   // required: which (issuer, policyId) may root a delegation
      { issuer: "oxdeai.policy-engine", policyId: "policy-production-v1" },
    ],
    mapActionToIntent: toGpuIntent,   // explicit amount, as in the guard setup tab
  },
  { tenancy: "single-tenant" }
);

// The acting identity comes from authentication, never from the request body.
const childContext = createTrustedExecutionContext({
  principalId: authenticatedPrincipalId,
  agentId:     resolveAgentId(authenticatedPrincipalId),   // e.g. "child-agent-002"
  adapterId:   "http-pep",
  depth:       1,   // derived from the real call chain
});

// Recipient binding: delegatee must equal the authenticated acting agent.
// On main (#350, not yet published) the guard enforces this itself, before replay
// consumption, state mutation or execution. @oxdeai/guard@2.0.0 does NOT check it,
// so on 2.0.0 this explicit check is required.
if (delegation.delegatee !== childContext.agentId) {
  throw new Error("Delegation was not issued to this agent");
}

await guard(
  childContext,
  { name: "provision_gpu", args: gpuRequest },
  async () => provisionGpu(gpuRequest),   // only runs if the chain and scope verify
  {
    delegation: {
      delegation,
      parentAuth,   // single hop: the parent is an AuthorizationV1, never another delegation
      parentScope: { tools: ["provision_gpu"], max_amount: 1_000_000_000n },   // parent's ceiling, from deployer config
    },
  }
);
// delegation_id and parent auth_id consumed atomically; replay → OxDeAIAuthorizationError
@oxdeai/core@2.0.0 and @oxdeai/guard@2.0.0 are published on npm. Guard-enforced delegation recipient binding (#350) is on main and not yet in a published release. View source on GitHub →

Works with the frameworks you already use.

All supported adapters delegate authorization to @oxdeai/guard and are exercised in CI.

LangGraph
@oxdeai/langgraph
Node execution gating in LangGraph agent graphs.
available
CrewAI
@oxdeai/crewai
Task-level authorization for CrewAI crews.
available
AutoGen
@oxdeai/autogen
Function call management for Microsoft AutoGen.
available
OpenAI Agents
@oxdeai/openai-agents
Tool-call interception for OpenAI Agents SDK.
available
OpenClaw
@oxdeai/openclaw
Guard integration for OpenClaw agent runtimes.
available

Each adapter ships its own test suite and is validated against @oxdeai/core in CI before publish.

Built for production enforcement.

OxDeAI is open source and Apache-2.0 licensed. Start integrating OxDeAI from source today.