Agent policy-as-code loads policy once at PaybondAgentRun.bind() by default. Long-lived processes — daemon workers, persistent LangGraph graphs, MCP servers — need to pick up policy changes without restarting the run or re-bootstrapping intents.
Hot-reload adds versioned policy snapshots and safe reload semantics on top of the existing registry and interceptor pipeline.
When you need hot-reload
| Scenario | Without reload | With hot-reload |
|---|---|---|
Edit paybond.policy.yaml on disk | No effect until re-bind | File watcher or manual reload applies changes |
| Org publishes new base policy | Tenants stale until restart | Gateway poll reloads effective inherited policy |
| Stricter cap mid-run | Old cap until re-bind | New cap applies to subsequent tool calls |
| In-flight tool call during reload | N/A | Completes under policy digest active at authorize time |
Prefer hot-reload when your agent process stays alive across many tool calls and you want GitOps-friendly policy updates without dropping the funded intent.
Policy snapshot model
Every loaded policy carries a versioned snapshot:
type PaybondPolicySnapshot = { digest: string; // sha256 of canonical policy JSON version: string; // policy name + digest short loadedAt: string; // RFC3339 source: "file" | "remote" | "effective"; registry: PaybondToolRegistry; };
PaybondAgentRun holds the active snapshot. Each authorization pins policyDigest for audit and evidence submission.
Reload triggers
1. File watcher (local dev / single-tenant deploy)
TypeScript:
const run = await paybond.agentRun.bind({ policyFile: "./paybond.policy.yaml", registry: policy.toToolRegistry(), policySnapshot: snapshot, bootstrap: policy.sandboxBootstrap({ operation: "travel.book_hotel" }), reload: { watch: true, debounceMs: 500 }, });
Python:
run = await paybond.agent_run.bind({ "policy_file": "./paybond.policy.yaml", "registry": registry, "policy_snapshot": snapshot, "bootstrap": policy.sandbox_bootstrap(operation="travel.book_hotel"), "reload": {"watch": True, "debounce_ms": 500}, })
On file change: parse → local validate → optional remote validate → atomic registry swap.
CLI bind records watch config for persisted runs:
paybond agent run bind --policy-file paybond.policy.yaml --watch --format json
Long-lived SDK processes should call bind() with reload.watch directly; the CLI persists reload.watch in .paybond/runs/<run_id>.json for status inspection.
2. Gateway poll (production / inherited policies)
For tenant overlays that extend an org base policy (enterprise policy inheritance):
reload: { poll: { intervalMs: 60_000, remote: true, resolveInheritance: true, }, },
Polls POST /v1/org-policies/{id}/effective?digest=current. When the digest differs, fetch → validate → swap.
3. Manual reload (CLI or SDK)
CLI for persisted runs:
paybond agent run reload-policy --run-id <id> --format json paybond agent run reload-policy --run-id <id> --policy-file ./paybond.policy.yaml --remote --format json
Flags:
| Flag | Purpose |
|---|---|
--policy-file | Override policy path (defaults to path stored at bind) |
--remote | Run Gateway POST /v1/policy/validate before applying |
--resolve-inheritance | Merge org base server-side for v2 overlays |
--allow-loosen | Permit higher caps or new side-effecting tools (default deny) |
SDK:
const result = await run.reloadPolicy({ file: "./paybond.policy.yaml", remote: true, }); run.on("policyReloaded", ({ previousDigest, newDigest }) => { /* ... */ }); run.on("policyReloadFailed", (error) => { /* ... */ });
Inspect reload state or middleware trace:
paybond agent run status --run-id <id> --format json paybond agent run trace --run-id <id> --format table
Example status fields:
{ "run_id": "...", "policy_digest": "sha256:...", "policy_loaded_at": "2026-06-29T12:00:00Z", "reload": { "watch": true, "last_reload_at": "2026-06-29T12:05:00Z" } }
Safe reload semantics
Critical rules (fail-closed):
- In-flight tool calls pin the policy digest at
authorizetime; evidence submit uses the same digest. - Reload during execute waits for in-flight interceptors to finish (ref count) before swap.
- Failed reload keeps the previous snapshot; emits structured error; no partial apply.
- Stricter policy applies immediately after swap to new tool calls.
- Loosening policy (higher caps, new side-effecting tools) requires
--allow-loosenorallowLoosen: true; default deny in production. - Intent scope — reload cannot change
intent_id/capability_token. If reloaded policy requires tools outside the bound intent, reload fails withintent_rebind_required.
Structured error codes:
| Code | Meaning |
|---|---|
loosening_denied | Policy relaxed caps or added side-effecting tools without approval |
intent_rebind_required | New allowed_tools drift from funded intent |
remote_validate_failed | Gateway rejected the policy document |
invalid_overlay | Poll reload requires v2 overlay with extends.org_policy_id |
MCP servers
Long-lived MCP processes support env-driven reload. See MCP server:
export PAYBOND_POLICY_FILE="./paybond.policy.yaml" export PAYBOND_POLICY_RELOAD="watch" # watch | poll | off export PAYBOND_POLICY_RELOAD_ALLOW_LOOSEN="0"
The MCP gate reloads the registry between tool invocations without restart.
Production checklist
- Bind with a policy file and track
policy_digestin run status. - Use
--remote(CLI) orremote: true(SDK) before accepting reloads in production. - For inherited policies, enable
resolveInheritanceon poll or manual reload. - Keep loosening disabled unless an operator explicitly approves
--allow-loosen. - Monitor
policyReloadFailedevents; failed reloads never partial-apply.
Related docs
- Agent policy-as-code — policy file format and bind-time loading
- Agent policy validate — server-authoritative checks before deploy
- Enterprise policy inheritance — org base + tenant overlay
- Agent middleware — run binding, interceptors, and evidence