Deny wins
A matching deny rule rejects the request regardless of allow rule order.
allowops is an open source operations gateway for AI agents. You define typed actions in a catalog, review it and sign it. Agents call only those actions through MCP. The gateway checks policy for every target host, runs batches and records an audit trail.
disk_usage
Codex→journal_grep
CLI→k8s_pods
Access paths
Actions in the example catalog (51)
How it works
Agents, the CLI and the console connect only to the gateway. It authenticates each request, selects hosts by label, checks policy for every target and dispatches in batches. Hosts connect out to the gateway; no inbound host port is required.
environment=demoenvironment=demoenvironment=demounlabeledname: docker_stop description: Stop one allowed application container. risk: write run_as: root params: container: type: enum values: [api, app] grace_seconds: type: int min: 0 max: 30 default: 10 exec: [/usr/bin/docker, stop, --time, '{grace_seconds}', '{container}'] timeout: 45s max_output: 16KB batch_size: 1 max_failures: 1
Action catalog
Each action is a fixed argument array plus typed parameters. An agent fills in parameters; it cannot compose commands. Hosts take commands from the signed catalog, never from a command string sent by an agent or the gateway.
enum, int, string (RE2), duration, path, url. Unknown fields and wrong types are rejected.{name} is substituted. Values starting with - are rejected.read, write and dangerous are policy attributes.Signing
Catalogs are validated and built in CI, signed with Ed25519 in a DSSE envelope, then published. You distribute the public keys to the gateway and each host. The gateway cannot add or revoke keys on a host.
Check fields, parameter types and path constraints. Try an action on a controlled test host as the current user.
allowops catalog validate ./catalog
The build records the Git commit and a UTC version. The private key stays in the CI credential store.
allowops catalog sign --in catalog.json …
The gateway accepts the publication. The host agent and the root helper each verify the signature before execution.
allowops catalog publish -f catalog.dsse.json
Policy
The gateway embeds OPA. Rules are structured data, not arbitrary Rego. Deny wins; no match and evaluation errors deny. If any target is denied or the count does not match, the whole request is rejected before dispatch.
One service account calls the three hosts labeled environment=demo. Switch scenarios to see the gateway decision.
// tools/call → disk_usage { "targets": { "selector": "environment=demo" }, "expected_count": 3, "reason": "check demo disk usage" }
rules: - name: readers-on-demo effect: allow roles: [ops-reader] operations: [execute_action] risks: [read] host_labels: { environment: demo } - name: deny-writes-on-demo effect: deny roles: ['*'] operations: [execute_action] risks: [write, dangerous] host_labels: { environment: demo }
environment=demoreaders-on-demoallowenvironment=demoreaders-on-demoallowenvironment=demoreaders-on-demoallow// tools/call → docker_stop { "targets": { "selector": "environment=demo" }, "expected_count": 3, "reason": "stop api container as requested", "params": { "container": "api" } }
rules: - name: readers-on-demo effect: allow roles: [ops-reader] operations: [execute_action] risks: [read] host_labels: { environment: demo } - name: deny-writes-on-demo effect: deny roles: ['*'] operations: [execute_action] risks: [write, dangerous] host_labels: { environment: demo }
environment=demodeny-writes-on-demodenyenvironment=demodeny-writes-on-demodenyenvironment=demodeny-writes-on-demodeny// tools/call → disk_usage { "targets": { "selector": "environment=demo" }, "expected_count": 2, "reason": "check demo disk usage" }
rules: - name: readers-on-demo effect: allow roles: [ops-reader] operations: [execute_action] risks: [read] host_labels: { environment: demo } - name: deny-writes-on-demo effect: deny roles: ['*'] operations: [execute_action] risks: [write, dangerous] host_labels: { environment: demo }
environment=demoreaders-on-demoallowenvironment=demoreaders-on-demoallowenvironment=demoreaders-on-demoallow// tools/call → disk_usage { "targets": { "hosts": ["demo-4"] }, "reason": "check demo disk usage" }
rules: - name: readers-on-demo effect: allow roles: [ops-reader] operations: [execute_action] risks: [read] host_labels: { environment: demo } - name: deny-writes-on-demo effect: deny roles: ['*'] operations: [execute_action] risks: [write, dangerous] host_labels: { environment: demo }
—no authorization labelsdenyA matching deny rule rejects the request regardless of allow rule order.
Operators set authorization labels at the gateway. Labels reported by hosts filter inventory but never grant access.
Selector calls require expected_count. A different actual count rejects the whole call.
Rules can limit time windows in a timezone and source CIDRs. Use separate accounts for reads and writes.
Console
The console uses the same API and permissions as the CLI and MCP. These are real screenshots of the local demo environment.

Online hosts, running jobs, and catalogs and tokens close to expiry.

Authorization labels, agent versions and loaded catalog versions.

The current signed catalog, its expiry and the risk of every action.

Per-host state, exit code, duration and whether output was stored.

Each audit event has a sequence number and hash, and can be exported and verified offline.
Details
Output is redacted before it leaves the host, then filtered and truncated. Built-in rules cover common secret formats; add your own patterns.
MCP results carry output_trust: untrusted. Host output may contain prompt injection and is never treated as instructions.
Audit records decisions, identifiers and output hashes, not output bodies. Verify exports with audit verify.
Hosts open no inbound port. Registration pins the gateway CA; later connections use mutual TLS and short-lived host certificates.
The agent runs as the allowops user. Root actions go through a fixed sudo rule to allowops-exec, which verifies the catalog again.
Tasks run in batch_size batches. Remaining hosts are skipped when failures exceed max_failures. Offline hosts are not queued.
Quick start
The Compose demo includes the gateway, PostgreSQL, SeaweedFS, an LDAPS directory and two hosts. The script generates credentials locally, publishes the example catalog and runs a read-only disk_usage job on both hosts.
git clone https://github.com/liubing0806/allowops.git && cd allowops/deploy/compose && ./quickstart-local.shRequires Linux amd64, Docker Compose, Go 1.27.3, OpenSSL, Git and Python 3 with PyYAML. Use the Helm chart for Kubernetes and .deb or .rpm packages for hosts; see installation.
# Define and publish a catalog allowops catalog validate ./catalog allowops catalog build --dir ./catalog --out catalog.json allowops catalog sign --in catalog.json --out catalog.dsse.json --key "$KEY_FILE" allowops catalog publish -f catalog.dsse.json # Label hosts; unlabeled hosts reject execution allowops hosts label demo-1 --set environment=demo # Simulate the policy, then replace it allowops policy test -f rules.yaml allowops policy apply -f rules.yaml --reason 'allow read-only access to demo' # Run the same action from a terminal allowops run disk_usage --selector environment=demo --expected-count 3 --reason 'disk check' allowops jobs output job-20261022-0010 --host demo-1 --stream stdout
Open source (MIT) and self-hosted. Gateway, host agent and CLI ship from the same release.
Version v0.1.0