The Agent Control Specification (ACS) is a stateless, deterministic policy decision runtime for agent security, built on the agent-hooks control contract.
The layering:
- agent-hooks defines the control contract: eight interception
points, the
AgentContextpayload, the three-verdict model (allow/deny/transform, with warnings and liftable denies), host obligations, composition, and the conformance test kit. - ACS, this repository, is a conformant interceptor: a host framework emits interception points through an agent-hooks emitter, and ACS evaluates the bound policy (manifest binding, Cedar, Rego or custom dispatchers, annotators, information-flow labels) and returns an agent-hooks verdict.
- Hosts and frameworks integrate agent-hooks once and gain ACS (or any other interceptor) without bespoke glue.
ACS decisions are deterministic: the same manifest, the same context,
the same annotations and the same dispatcher results give the same
verdict. A dispatcher is the host-trusted code that runs an annotator
or a policy, so a dispatcher that reads a clock or the network breaks
replay. The bundled Rego dispatcher blocks http.send, and it blocks
the clock and random builtins (time.now_ns, rand.intn,
uuid.rfc4122, opa.runtime), so a policy that calls one fails
closed. Escalation is expressed natively as a liftable deny (deny +
approval); warnings ride on allow.
ACS computes verdicts and nothing else. It does not sandbox, it does not isolate processes, and it does not mediate a path that bypasses an intervention point. It applies no transform, resolves no approval and keeps no record. Enforcement is the job of the agent-hooks emitter and the host that owns it: the emitter blocks a denied action and hands back the transformed target the host must use. The host is fully trusted; conformance is a test result, not a certification. The threat model is in specification section 21 and in the agent-hooks threat model.
Security: see SECURITY.md for the trust model and how to report a vulnerability.
| Path | Contents |
|---|---|
engine/ |
Rust evaluation core (agent-control-spec crate): manifest, dispatchers, annotators, policy-output normalization, the AcsInterceptor |
sdk/ffi/ |
C ABI over the engine (agent-control-spec-ffi), which the .NET binding calls |
sdk/python/ |
Python binding: agent_control_spec package wrapping the engine as an agent_hooks interceptor |
sdk/remote-policy/python/ |
Optional agent-control-spec-remote-policy Python distribution for remote policy downloads and pluggable authentication; not installed with the base SDK |
sdk/node/ |
Node binding: @responsibleai/agent-control-spec |
sdk/dotnet/ |
.NET binding: ResponsibleAI.AgentControlSpec |
generator/ |
Optional acs-policy-gen authoring tool. Produces draft manifests and Rego from prose, with parser checks and ACS validation |
spec/ |
The ACS specification (policy plane) and schemas |
policy/ |
Cedar and Rego policy libraries |
fixtures/ |
Evaluation fixtures |
examples/ |
Example manifests and hosts; python_composition/ runs in CI and bank_agent/ feeds the engine tests and the benchmark |
docs/EXTRACTION.md |
Provenance map from the previous tree |
Rust builds require Rust 1.89 or newer. See manifest parsing for YAML typing rules, host-controlled parsing budgets and parser dependency details.
Rust: add agent-control-spec = "0.4.0-beta.1" to Cargo.toml. Cargo
does not match a pre-release from a bare version requirement, so name
the exact string while the crate is pre-release. The bundled annotator
dispatchers sit behind the default-dispatchers feature; the
zero-config constructor below needs it. A tokio host that wants the
emitter's timeout to bound ACS adds the tokio feature
(features = ["default-dispatchers", "tokio"]): the interceptor then
evaluates on a blocking worker so the timeout can fire, and the feature
turns on the emitter's own tokio-timeout.
[dependencies]
agent-control-spec = { version = "0.4.0-beta.1", features = ["default-dispatchers"] }
agent-hooks-sdk = "0.1.0-beta.2"
serde_json = "1"From a manifest to a registered interceptor:
use agent_control_spec::ActivatedPolicy;
use agent_hooks::{AgentContextBuilder, EnforcementMode, InterceptionEmitter};
use serde_json::json;
async fn guard() -> Result<(), Box<dyn std::error::Error>> {
// Once per policy version: read the manifest, load and compile its
// Rego, and ready every bound intervention point.
let policy = ActivatedPolicy::activate_from_path("manifest.yaml")?;
// Register the policy with an agent-hooks emitter. The emitter
// applies verdicts; ACS only computes them.
let mut emitter = InterceptionEmitter::new(EnforcementMode::Enforce, None);
emitter.register(Box::new(policy.interceptor()));
// Emit one pre_tool_call context. A deny returns Err; an allow or
// transform returns the target the tool call must use.
let mut builder = AgentContextBuilder::new("demo-agent", "my-framework", "session-1");
let mut ctx = builder.pre_tool_call("call-1", "wire_transfer", json!({"amount": 100}));
match emitter.emit(&mut ctx).await {
Ok(outcome) => println!("proceed with {}", outcome.target),
Err(blocked) => println!("blocked: {blocked}"),
}
Ok(())
}activate_from_path installs the bundled dispatchers: Rego in process,
Cedar through the built-in evaluator, and the bundled annotators, which
make HTTPS calls during evaluation when the manifest declares
annotators. A host that wants its own dispatchers uses
ActivatedPolicy::activate_with or Runtime::new, and
AcsInterceptor::new(runtime) wraps a runtime directly.
Python: SDK quickstart and composing ACS controls through Agent Hooks.
Node: npm install @responsibleai/agent-control-spec, see the
Node wrapper. A plain install gets the newest
release, pre-releases included; the channel tags alpha, beta and
rc exist for pinning to one line.
.NET: dotnet add package ResponsibleAI.AgentControlSpec --prerelease,
see the .NET wrapper.
Current version: 0.4.0-beta.1, the first beta of the re-based runtime.
It depends on agent-hooks 0.1.0-beta.2.