Policy Bundles¶
Bundle structure¶
A policy bundle is a gzip-compressed tar archive produced by autonomy policy build.
Archive contents:
bundle.tar.gz
├── manifest.json ← bundle metadata
└── *.rego ← Open Policy Agent source files (alphabetical order)
manifest.json fields:
Field |
Type |
Required |
Description |
|---|---|---|---|
|
semver string |
Yes |
Bundle version, e.g. |
|
semver range |
No |
Constraint on runtime binary version |
|
|
Set by builder |
Hash of sorted filenames + contents |
|
string |
No |
Human-readable identifier |
|
RFC3339 |
Set by builder |
Build timestamp (UTC) |
Build a bundle:
autonomy policy build \
--in ./policies \
--out bundle.tar.gz \
--version 1.2.0 \
--name my-policy \
--runtime-version ">=0.1.0 <2.0.0"
Expected output:
policy build: OK
version: 1.2.0
hash: sha256:cd1525064...
runtime-version: >=0.1.0 <2.0.0
output: bundle.tar.gz
Runtime compatibility check¶
required_runtime_version is a space-separated list of semver constraints:
">=0.1.0 <2.0.0" # runtime must satisfy both constraints
">=1.0.0" # runtime must be at least 1.0.0
"" # no constraint (default) — always compatible
Supported operators: >=, >, <=, <, =.
The current runtime version is 0.1.0.
Incompatible bundle behavior:
autonomy policy load --bundle incompatible.tar.gz
policy load: REJECTED — bundle requires runtime >=99.0.0, have 0.1.0
The load is rejected. Both the current and lkg slots remain unchanged.
A policy.bundle.rejected telemetry event is emitted.
The currently active policy continues to serve requests.
Loading and the managed cache¶
policy load installs a bundle into the managed cache at
$XDG_CACHE_HOME/autonomyops/policy/managed (override with --manager-dir):
autonomy policy load \
--bundle bundle.tar.gz \
--manager-dir /opt/autonomy/policy
policy load: OK — version=1.2.0 hash=sha256:c...
The managed cache layout:
/opt/autonomy/policy/
├── current/
│ ├── bundle.tar.gz
│ └── meta.json ← version, digest, loaded_at, runtime_constraint
└── lkg/
├── bundle.tar.gz
└── meta.json
The runtime reads from this directory at startup via --policy-dir.
LKG (last-known-good)¶
Each policy load promotes the previous current to lkg before installing
the new bundle.
Before load: After load:
current → v1.0.0 current → v1.1.0
lkg → (none) lkg → v1.0.0
Before second load: After second load:
current → v1.1.0 current → v1.2.0
lkg → v1.0.0 lkg → v1.1.0
LKG holds exactly one bundle (the one immediately before current). Older versions are not retained.
When LKG is used: LKG is a recovery reference. The runtime does not automatically fall back to LKG on policy evaluation errors; the operator must manually promote LKG to current by loading the LKG bundle explicitly.
To inspect both slots:
autonomy policy status --manager-dir /opt/autonomy/policy
Current: version=1.2.0 digest=sha256:cd1525064... loaded=2026-02-27T20:00:00Z
LKG: version=1.1.0 digest=sha256:ab3f8e112... loaded=2026-02-27T19:00:00Z
Stale bundle detection¶
A bundle that has not been updated in more than 30 days is flagged as stale.
Current: version=1.0.0 digest=sha256:... loaded=2025-12-01T00:00:00Z [STALE]
A policy.bundle.stale telemetry event is emitted when the status command
detects staleness. This does not affect runtime behavior; it is an advisory only.
Policy evaluation (OPA/Rego)¶
The evaluator compiles bundle Rego modules at load and evaluates per request
using OPA with query data.autonomy.allow.
Input shape:
{
"kind": "tool.echo",
"params": { "message": "hello" }
}
Decision rule:
allow == true=> request allowedmissing/false/invalid result => request denied (fail-closed)
Example Rego deny policy:
package autonomy
default allow := false
allow if {
input.kind == "tool.echo"
}
Attaching a bundle to an OCI image¶
Policy bundles travel alongside agent images as OCI sidecar artifacts. See oci.md for the attachment workflow.
After attaching, retrieve and load the bundle in one step:
autonomy oci pull-policy \
--image localhost:5000/agent:v1 \
--out /tmp/bundle.tar.gz
autonomy policy load --bundle /tmp/bundle.tar.gz
Legacy formats (deprecated)¶
These formats are not used by the demo pipeline or CI acceptance gates. They are retained for backward compatibility only and will emit a deprecation warning when used.
manifest.toml (legacy directory format)¶
Early versions supported loading a policy bundle directly from a directory
containing manifest.toml:
policies/
├── manifest.toml ← legacy metadata (version, name, policy_ref)
└── *.rego
manifest.toml schema:
Field |
Description |
|---|---|
|
Bundle version string (e.g. |
|
Human-readable identifier |
|
OCI reference the bundle was pushed to |
|
RFC3339 timestamp |
This path is still reachable via autonomy policy inspect --dir <dir> for
development convenience. A deprecation warning is printed to stderr on every
use:
WARNING [policy]: loading bundle from manifest.toml (legacy/deprecated format).
Migrate to canonical .tar.gz: autonomy policy build --in <dir> --out bundle.tar.gz --version <version>
Migration: Replace manifest.toml with a canonical .tar.gz bundle using
autonomy policy build. The policy_bundle_version and name fields map
directly to --version and --name.
Raw directory loading (policy inspect --dir)¶
The --dir flag on policy inspect accepts a directory containing either
manifest.json (preferred) or manifest.toml (legacy fallback). This flag is
deprecated; use --bundle with a .tar.gz archive instead.
Updating policy without downtime¶
The runtime reads the active bundle at startup. To apply a new bundle to a running instance:
Load the new bundle:
autonomy policy load --bundle new.tar.gzRestart the runtime (sends SIGTERM, waits for in-flight requests to complete)
There is no hot-reload mechanism in 0.1.0.