Summary
APL is an attribute policy language purpose-built for AI agent systems. It provides a declarative way to evaluate attributes such as identity, permissions, session state, delegation context, metadata, and content, then apply explicit effects such as allow, deny, taint, transform, redact, mask, invoke plugins, or call external policy engines.
APL also includes sequencing and orchestration semantics, allowing policy actions to run in ordered pipelines, conditionally, or in parallel where appropriate. This enables policies that do more than authorize requests. They can coordinate multi-step enforcement workflows across the full agent execution lifecycle.
Through these capabilities, APL controls what agents can see, do, and share, with identity-aware access control, field-level data transforms, session taint tracking, token delegation, and composable integration with existing policy engines such as OPA, Cedar, AuthZEN, and NeMo Guardrails.
APL is built on CMF (Contextual Message Format) and runs inside CPEX (ContextForge Plugin Extensibility Framework), but the policy language itself is independent. It evaluates attributes and produces effects that any host can enforce.
APL Specification
The Problem
Enterprises deploying AI agents face a class of security challenges that traditional API security wasn't designed for:
Over-privileged tool access — An agent calling an HR API gets the full record, including fields the end user should never see. There's no field-level filtering based on who the user is or what role they hold.
No data flow control — Once an agent reads sensitive data, nothing prevents it from passing that data to another tool, writing it to a log, or including it in an email. PII leaks aren't from breaches — they're from normal agent workflows.
Identity confusion — The agent authenticates to the gateway, but the user's identity and permissions are lost by the time a tool is called. Policy decisions are made against the agent's credentials, not the human's.
Delegation without narrowing — When an agent calls a downstream service on behalf of a user, it forwards its own broad token. There's no standard way to mint a scoped, short-lived credential for just that one operation.
No session awareness — Each tool call is evaluated in isolation. The system has no memory that the agent already accessed PII two calls ago, and no way to restrict what happens next based on that history.
Policy fragmentation — Organizations use different policy engines for different concerns (OPA for authorization, NeMo for content safety, Cedar for fine-grained access). There's no way to compose them in a single evaluation pipeline over a common data model.
What APL Does
APL is a predicate-and-effects language. Every rule consists of a predicate evaluated against the current execution context, and one or more effects that execute when the predicate matches.
Organizes policy around agentic entities
Tools, prompts, resources, and LLMs are first-class objects with routes, conditions, and field-level transforms:
global:
policies:
all:
description: "Baseline authentication requirement"
policy:
- require(authenticated)
pii:
description: "PII data access controls"
metadata:
owner: security-team
severity: high
policy:
- require(perm.pii_access)
post_policy:
- exists(result.ssn): taint(PII, session)
routes:
- tool: get_compensation
meta:
tags: [pii]
args:
employee_id: "str"
include_ssn: "bool"
policy:
- args.include_ssn & !perm.view_ssn: deny
result:
salary: "int | taint(PII, session) | redact(!role.hr)"
ssn: "str | taint(PII, session) | redact(!perm.view_ssn)"
employee_id: "str | mask(4)"
internal_notes: "omit"
Composes hard and soft policy engines
APL conditions can dispatch to OPA, Cedar, AuthZEN, or NeMo Guardrails — with configurable side effects per decision. The same attribute context feeds every engine:
routes:
- tool: get_compensation
meta:
tags: [pii]
args:
employee_id: "str"
include_ssn: "bool"
policy:
# APL built-in — fast attribute checks (<1ms, Rust)
- args.include_ssn & !perm.view_ssn: deny
# OPA — complex authorization logic in Rego
- opa:
endpoint: "http://opa-sidecar:8181"
package: "hr.compensation"
input_namespaces: [subject, delegation, session]
result:
salary: "int | taint(PII, session) | redact(!role.hr)"
ssn: "str | taint(PII, session) | redact(!perm.view_ssn)"
employee_id: "str | mask(4)"
- tool: send_email
policy:
# APL — session taint blocks data exfiltration
- session.labels contains "PII": deny
# NeMo — scan email body for leaked PII
- nemo(args.body):
config_id: "pii-detection"
on_match: deny
APL is not a replacement for OPA, Cedar, or NeMo. It's the orchestration layer that brings them together — one route, multiple engines, one pipeline. Each engine evaluates against the same CMF-normalized attribute context. APL handles what happens with the decision (deny, taint, transform).
Decides AND modifies
APL goes beyond allow/deny. Policy rules can transform the data flowing through the pipeline — field-level redaction, masking, omission, and session taint tracking:
result:
salary: "int | taint(PII, session) | redact(!role.hr)"
ssn: "str | taint(PII, session) | redact(!perm.view_ssn)"
employee_id: "str | mask(4)"
internal_notes: "omit"
Each pipe chain reads left to right: validate the type, taint the session, then conditionally redact based on caller attributes. The same tool returns different data to different callers — zero changes to the tool itself.
Session-aware security
Labels accumulate monotonically across tool calls within a session. Once an agent touches sensitive data, the session is tainted and downstream actions are restricted:
1. Agent calls get_compensation → salary accessed → session tainted with [PII]
2. Agent calls send_email → session.labels contains "PII" → DENIED
3. Agent calls display_compensation → view action → ALLOWED (no forwarding)
Session taint is the mechanism that prevents data exfiltration through normal agent workflows. Traditional per-request authorization can't do this — it evaluates each call in isolation.
Pre-invoke and post-invoke policy
Policy evaluation happens in two phases, separated by the actual tool call:
Pre-invoke — Can the caller make this request?
policy:
- args.include_ssn & !perm.view_ssn: deny
- delegation.depth > 2: deny
Post-invoke — Given what the tool returned, should the response be allowed?
post_policy:
- exists(result.ssn) & !perm.view_ssn: deny
- session.labels contains "PII" & !perm.pii_access: deny
Post-invoke policy enables decisions based on actual results — not just what was requested.
Attribute-based evaluation
Every decision is made by evaluating attributes from across the system:
| Namespace |
Source |
Examples |
| Subject |
Identity token (JWT, mTLS) |
role.hr, perm.view_ssn, subject.teams |
| Delegation |
Token exchange chain |
delegation.depth, delegation.origin, delegated |
| Session |
Accumulated state |
session.labels, session.tool_calls, session.cost |
| Meta |
Host-provided metadata |
meta.tags, meta.scope, meta.properties.owner |
| Content |
Tool args / results |
args.employee_id, result.salary |
All attributes are collected into an AttributeBag — a flat namespace that any predicate can reference. The same bag feeds APL's built-in evaluator, OPA, Cedar, NeMo, and AuthZEN.
What This Enables
| Capability |
What it does |
| Identity Resolution |
Separates agent credentials from user identity. User's JWT → roles, permissions, teams in the attribute bag. |
| Result Transforms |
Field-level redaction, masking, omission on tool results — driven by attributes, not hardcoded logic. |
| Session Taint Tracking |
Labels accumulate across tool calls. PII access taints the session; forwarding is blocked. |
| Token Delegation |
RFC 8693 token exchange mints narrowed, audience-scoped credentials for downstream tools. |
| Policy Composition |
OPA, Cedar, NeMo, AuthZEN in the same pipeline over the same attribute context. |
Built On
-
CMF (Contextual Message Format) — Protocol-agnostic message format that normalizes tool calls, identity, delegation, session, and security labels into a single envelope. CMF is what makes APL portable across MCP, A2A, REST, and gRPC.
-
CPEX (ContextForge Plugin Extensibility Framework) — Hook-based plugin runtime that embeds enforcement points at every stage of the agentic lifecycle. CPEX is where APL plugs in — alongside OPA, NeMo, and custom plugins.
30-Second Demo
An engineer and an HR manager both ask an AI agent to look up the same employee's compensation:
Engineer (Alice): HR Manager (Bob):
salary: [REDACTED] salary: 125000
employee_id: ******1234 employee_id: ******1234
internal_notes: (omitted) internal_notes: (omitted)
SSN request: DENIED SSN: 123-45-6789
Bob then tries to email the data externally:
send_email: DENIED (session tainted with PII)
Alice views a compensation summary:
display_compensation: ALLOWED
Same agent, same gateway, same tool. Different results based on identity, permissions, and session history. Zero changes to the HR tool.
Specification
The full APL DSL specification is available at APL Specification. It covers:
- Policy structure (global policies, defaults, routes, evaluation order)
- Predicate language (require, exists, comparisons, set operators, logical combinators)
- Effects (deny, allow, taint, plugin, sequential/parallel orchestration)
- Pipe chains (field-level validation and transforms)
- External PDP integration (OPA, Cedar, AuthZEN, NeMo with reaction model)
- Complete EBNF grammar
The specification documents implemented behavior. The APL core is built in Rust with Python (PyO3) bindings, providing sub-millisecond evaluation per request.
Another document is the unified configuration doc which describes how APL fits with the broader CPEX configuration.
Get Involved
We're looking for feedback on:
- The policy language design — Does the predicate-and-effects model cover your agentic security use cases?
- External PDP integration — Is the OPA/Cedar/AuthZEN/NeMo integration model sufficient for your environment?
- Session taint semantics — Are monotonic session labels the right primitive for data flow control?
- Attribute namespaces — Are there attributes missing from the AttributeBag that your policies need?
- Post-invoke policy — What post-invoke decisions do you need beyond what
post_policy provides?
Comments, questions, and contributions welcome.
Summary
APL is an attribute policy language purpose-built for AI agent systems. It provides a declarative way to evaluate attributes such as identity, permissions, session state, delegation context, metadata, and content, then apply explicit effects such as allow, deny, taint, transform, redact, mask, invoke plugins, or call external policy engines.
APL also includes sequencing and orchestration semantics, allowing policy actions to run in ordered pipelines, conditionally, or in parallel where appropriate. This enables policies that do more than authorize requests. They can coordinate multi-step enforcement workflows across the full agent execution lifecycle.
Through these capabilities, APL controls what agents can see, do, and share, with identity-aware access control, field-level data transforms, session taint tracking, token delegation, and composable integration with existing policy engines such as OPA, Cedar, AuthZEN, and NeMo Guardrails.
APL is built on CMF (Contextual Message Format) and runs inside CPEX (ContextForge Plugin Extensibility Framework), but the policy language itself is independent. It evaluates attributes and produces effects that any host can enforce.
APL Specification
The Problem
Enterprises deploying AI agents face a class of security challenges that traditional API security wasn't designed for:
Over-privileged tool access — An agent calling an HR API gets the full record, including fields the end user should never see. There's no field-level filtering based on who the user is or what role they hold.
No data flow control — Once an agent reads sensitive data, nothing prevents it from passing that data to another tool, writing it to a log, or including it in an email. PII leaks aren't from breaches — they're from normal agent workflows.
Identity confusion — The agent authenticates to the gateway, but the user's identity and permissions are lost by the time a tool is called. Policy decisions are made against the agent's credentials, not the human's.
Delegation without narrowing — When an agent calls a downstream service on behalf of a user, it forwards its own broad token. There's no standard way to mint a scoped, short-lived credential for just that one operation.
No session awareness — Each tool call is evaluated in isolation. The system has no memory that the agent already accessed PII two calls ago, and no way to restrict what happens next based on that history.
Policy fragmentation — Organizations use different policy engines for different concerns (OPA for authorization, NeMo for content safety, Cedar for fine-grained access). There's no way to compose them in a single evaluation pipeline over a common data model.
What APL Does
APL is a predicate-and-effects language. Every rule consists of a predicate evaluated against the current execution context, and one or more effects that execute when the predicate matches.
Organizes policy around agentic entities
Tools, prompts, resources, and LLMs are first-class objects with routes, conditions, and field-level transforms:
Composes hard and soft policy engines
APL conditions can dispatch to OPA, Cedar, AuthZEN, or NeMo Guardrails — with configurable side effects per decision. The same attribute context feeds every engine:
APL is not a replacement for OPA, Cedar, or NeMo. It's the orchestration layer that brings them together — one route, multiple engines, one pipeline. Each engine evaluates against the same CMF-normalized attribute context. APL handles what happens with the decision (deny, taint, transform).
Decides AND modifies
APL goes beyond allow/deny. Policy rules can transform the data flowing through the pipeline — field-level redaction, masking, omission, and session taint tracking:
Each pipe chain reads left to right: validate the type, taint the session, then conditionally redact based on caller attributes. The same tool returns different data to different callers — zero changes to the tool itself.
Session-aware security
Labels accumulate monotonically across tool calls within a session. Once an agent touches sensitive data, the session is tainted and downstream actions are restricted:
Session taint is the mechanism that prevents data exfiltration through normal agent workflows. Traditional per-request authorization can't do this — it evaluates each call in isolation.
Pre-invoke and post-invoke policy
Policy evaluation happens in two phases, separated by the actual tool call:
Pre-invoke — Can the caller make this request?
Post-invoke — Given what the tool returned, should the response be allowed?
Post-invoke policy enables decisions based on actual results — not just what was requested.
Attribute-based evaluation
Every decision is made by evaluating attributes from across the system:
role.hr,perm.view_ssn,subject.teamsdelegation.depth,delegation.origin,delegatedsession.labels,session.tool_calls,session.costmeta.tags,meta.scope,meta.properties.ownerargs.employee_id,result.salaryAll attributes are collected into an AttributeBag — a flat namespace that any predicate can reference. The same bag feeds APL's built-in evaluator, OPA, Cedar, NeMo, and AuthZEN.
What This Enables
Built On
CMF (Contextual Message Format) — Protocol-agnostic message format that normalizes tool calls, identity, delegation, session, and security labels into a single envelope. CMF is what makes APL portable across MCP, A2A, REST, and gRPC.
CPEX (ContextForge Plugin Extensibility Framework) — Hook-based plugin runtime that embeds enforcement points at every stage of the agentic lifecycle. CPEX is where APL plugs in — alongside OPA, NeMo, and custom plugins.
30-Second Demo
An engineer and an HR manager both ask an AI agent to look up the same employee's compensation:
Same agent, same gateway, same tool. Different results based on identity, permissions, and session history. Zero changes to the HR tool.
Specification
The full APL DSL specification is available at APL Specification. It covers:
The specification documents implemented behavior. The APL core is built in Rust with Python (PyO3) bindings, providing sub-millisecond evaluation per request.
Another document is the unified configuration doc which describes how APL fits with the broader CPEX configuration.
Get Involved
We're looking for feedback on:
post_policyprovides?Comments, questions, and contributions welcome.