From a3775b69165fd2f1284b1c5ebc422d0f5a2571e7 Mon Sep 17 00:00:00 2001 From: knhn1004 <49494541+knhn1004@users.noreply.github.com> Date: Tue, 5 May 2026 16:21:37 -0700 Subject: [PATCH] =?UTF-8?q?chore:=20deprecate=20policies/default.yaml=20?= =?UTF-8?q?=E2=80=94=20registry-first=20first-boot=20policy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the bundled five-gate policies/default.yaml. The Dockerfile no longer COPYs it, no longer sets AGENTLOCK_POLICY by default, and the daemon falls back to its built-in minimal policy in main.go (single rogue.destructive-bash gate, monitor mode) so every session still has a non-empty policy hash to attest against. Operators are expected to populate real coverage from the openagentlock/rules registry: agentlock rules sync agentlock rules install rogue.destructive-bash agentlock rules install rogue.secret-read agentlock rules install rogue.net-egress ... This matches the pivot already documented in docs/guide/policies.md (merged earlier as PR #47): rules-registry-first, not five-fixed-gates. The README "five gates" section, docs/index.md gate-card, and docs/guide/policies.md are all rewritten around the registry-first flow. The minimal in-binary fallback survives so a daemon spun up zero-conf via `docker run ghcr.io/openagentlock/agentlockd:latest` still has *some* policy to attest sessions against. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 27 +++++---- control-plane/Dockerfile | 7 ++- control-plane/cmd/control-plane/main.go | 12 +++- docs/guide/policies.md | 53 ++++------------ docs/index.md | 6 +- policies/default.yaml | 81 ------------------------- 6 files changed, 47 insertions(+), 139 deletions(-) delete mode 100644 policies/default.yaml diff --git a/README.md b/README.md index a2563f3..eb72127 100644 --- a/README.md +++ b/README.md @@ -110,19 +110,25 @@ Three languages, one repo: See [Architecture overview](https://openagentlock.github.io/OpenAgentLock/architecture/overview/) for the why behind the split. -## The five gates +## Policy — registry-first -Every install ships [`policies/default.yaml`](policies/default.yaml) with five gates in monitor mode: +OpenAgentLock ships with a minimal first-boot policy (a single `rogue.destructive-bash` gate in monitor mode) so every session has *some* policy hash to attest against. Real coverage comes from the [openagentlock/rules](https://github.com/openagentlock/rules) registry — install whichever rules match your threat model: -| Gate | What it catches | -|---|---| -| `supply-chain.pkg-install` | `pip install`, `npm install`, `brew install`, `cargo install` | -| `supply-chain.untrusted-mcp` | MCP server with an unpinned public key | -| `rogue.secret-read` | reads of `.env`, `~/.ssh`, `~/.aws/credentials`, anywhere a secret-shaped path appears | -| `rogue.net-egress` | `curl`, `wget`, MCP HTTP tools | -| `rogue.destructive-bash` | `rm -rf`, `git push --force`, `DROP TABLE`, `kubectl delete` | +```bash +agentlock rules sync # tap the upstream registry +agentlock rules search exfil # browse by keyword +agentlock rules install rogue.destructive-bash # land a gate in the live policy +agentlock rules install exfil.curl-with-env +agentlock rules install rogue.secret-read +``` + +You can also tap a private registry (any Git repo with the same layout) for org-internal rules: + +```bash +agentlock rules add https://github.com/your-org/your-rules.git +``` -See [Policies and the five gates](https://openagentlock.github.io/OpenAgentLock/guide/policies/) for the rule schema and authoring rules. +See [Policies and rules](https://openagentlock.github.io/OpenAgentLock/guide/policies/) for the schema and authoring guide. ## Repository layout @@ -133,7 +139,6 @@ control-plane/ Go HTTP service in Docker — ghcr.i Dockerfile, docker-compose.yml dashboard-ui/ Vite SPA embedded into the Go binary ledger/ Rust crate (lib + cdylib + staticlib) — openagentlock-ledger -policies/default.yaml baseline policy shipped with every install docs/ MkDocs Material site (deployed to openagentlock.github.io/OpenAgentLock) assets/ logo, favicon, social card docker-compose.yml one-command control-plane bring-up diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index b7f0d6c..e652186 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -59,11 +59,14 @@ RUN install -d -m 0700 -o 65532 -g 65532 /out/agentlock-home FROM gcr.io/distroless/cc-debian12 AS runtime COPY --from=go-builder /out/control-plane /usr/local/bin/agentlockd COPY --from=go-builder --chown=65532:65532 /out/agentlock-home /var/lib/agentlock -COPY policies/default.yaml /etc/agentlock/policies/default.yaml +# No bundled policy file — daemon falls back to the built-in +# minimal monitor-mode policy in main.go (single rogue.destructive-bash +# gate). Operators install community rules via `agentlock rules sync` +# + `agentlock rules install `. To pin a custom file, mount it +# and set AGENTLOCK_POLICY=/path/to/policy.yaml. ENV AGENTLOCK_LISTEN=0.0.0.0:7878 ENV AGENTLOCK_DASHBOARD_LISTEN=0.0.0.0:7879 ENV AGENTLOCK_HOME=/var/lib/agentlock -ENV AGENTLOCK_POLICY=/etc/agentlock/policies/default.yaml ENV AGENTLOCK_IN_CONTAINER=1 # Default to unattested so `docker run` works zero-conf. TUI + dashboard # show the red "UNATTESTED — LEDGER NOT SIGNED" banner. Override with diff --git a/control-plane/cmd/control-plane/main.go b/control-plane/cmd/control-plane/main.go index 84f556e..ffca0ef 100644 --- a/control-plane/cmd/control-plane/main.go +++ b/control-plane/cmd/control-plane/main.go @@ -157,8 +157,13 @@ func splitHostPort(addr string) (host, port string, ok bool) { } // loadPolicy reads $AGENTLOCK_POLICY if set; otherwise returns a built-in -// safe default (monitor mode, destructive-bash only) so the daemon always -// starts with *some* policy bound to session attestations. +// minimal default (monitor mode, single destructive-bash gate) so the +// daemon always starts with *some* policy bound to session attestations. +// The first-boot expectation is that operators run +// `agentlock rules sync && agentlock rules install ` against the +// openagentlock/rules registry to populate real coverage. The bundled +// `policies/default.yaml` was deprecated and removed — registry-first +// is the new shape (see docs/guide/policies.md). func loadPolicy(path string) (*policy.Policy, error) { if path == "" { return policy.LoadBytes([]byte(defaultPolicyYAML)) @@ -171,6 +176,9 @@ func loadPolicy(path string) (*policy.Policy, error) { return policy.Load(f) } +// defaultPolicyYAML is the minimal first-boot policy. It exists only so +// every session has a non-empty policy hash to attest against. Operators +// are expected to layer real coverage from openagentlock/rules on top. const defaultPolicyYAML = ` version: 1 mode: monitor diff --git a/docs/guide/policies.md b/docs/guide/policies.md index 9c2e068..5771495 100644 --- a/docs/guide/policies.md +++ b/docs/guide/policies.md @@ -66,50 +66,23 @@ If a rule id collides between two registries the CLI errors out and asks you to When the catalog doesn't have what you need, the [openagentlock/skills](https://github.com/openagentlock/skills) toolkit ships agent skills (Claude Code, Cursor, Codex) that turn natural-language intent into a `rule.yaml` and run `agentlock rules install` to land it. See the `block-pattern` skill for the canonical "block this command shape" flow. -## The five built-in defaults +## First-boot policy -When the daemon boots, it loads `policies/default.yaml`, which ships these five gates in monitor mode. They are intentionally narrow — most operators leave them on and add registry rules on top. +When the daemon boots without `AGENTLOCK_POLICY` pointing at a custom file, it loads a built-in minimal policy: a single `rogue.destructive-bash` gate in monitor mode. This exists only so every session has a non-empty policy hash to attest against — it is **not** meant as production coverage. The previously-bundled `policies/default.yaml` (with five hardcoded gates) was deprecated and removed. -
+Real coverage comes from the registry. Recommended starting set: -
-#### Package install -`supply-chain.pkg-install` - -`pip install`, `npm install`, `brew install`, `cargo install`. Catches typosquats and build-time exfiltration. -
- -
-#### Untrusted MCP -`supply-chain.untrusted-mcp` - -MCP server with an unpinned public key. Fingerprints land in `/v1/mcp/pin`. -
- -
-#### Secret reads -`rogue.secret-read` - -Reads of `.env`, `~/.ssh`, `~/.aws/credentials`, anywhere a secret-shaped path appears in the command. -
- -
-#### Network egress -`rogue.net-egress` - -`curl`, `wget`, MCP HTTP tools. Pre-execution. -
- -
-#### Destructive bash -`rogue.destructive-bash` - -`rm -rf`, `git push --force`, `DROP TABLE`, `kubectl delete`. -
- -
+```bash +agentlock rules install rogue.destructive-bash # tighter regex than the bootstrap gate +agentlock rules install rogue.secret-read # deny reads of .env / .ssh / .aws / credentials +agentlock rules install rogue.net-egress # block curl/wget/POST shapes +agentlock rules install supply-chain.npm-untrusted # block installs from URL/git/tarball +agentlock rules install supply-chain.pip-untrusted # same for pip / poetry / uv +agentlock rules install exfil.curl-with-env # catch $ENV_VAR exfil shapes +agentlock rules install rogue.git-force-push # deny force-push to main/develop/release +``` -The community registry has tighter / opinionated variants of several of these — e.g. `rogue.git-force-push` (only deny force-push to main / develop / release), `exfil.curl-with-env` (catch the `$ENV_VAR` exfil shape specifically), `rogue.eval-untrusted` (deny dynamic-eval shells). Install whichever match your threat model. +Browse the full catalog at . Pin a private registry alongside the upstream for org-internal rules — see the section above. ## Authoring rules from scratch diff --git a/docs/index.md b/docs/index.md index 21f92a5..fdc07fb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -40,10 +40,10 @@ Interactive multi-select. Posts to `/v1/install/plan`, renders the diff, applies
-#### Five baseline gates -`policies/default.yaml` +#### Registry-first policy +`agentlock rules install` -Package install, untrusted MCP, secret reads, network egress, destructive bash. Ship in monitor mode by default. +First-boot policy is intentionally minimal (one `rogue.destructive-bash` gate in monitor mode). Real coverage comes from the [openagentlock/rules](https://openagentlock.github.io/rules/) registry — `agentlock rules sync && agentlock rules install ` lands gates in the live policy with a fresh hash.
diff --git a/policies/default.yaml b/policies/default.yaml deleted file mode 100644 index 3faa52d..0000000 --- a/policies/default.yaml +++ /dev/null @@ -1,81 +0,0 @@ -# Default policy — five baseline gates in monitor mode. -# See docs/POLICY.md for the schema. -# -# Top-level mode = monitor. Every matched rule logs `match:monitor` -# and the verdict is forced to `allow`; nothing is blocked. Flip -# individual rules (or the whole policy) to enforce once you've -# reviewed the activity log in the dashboard. - -version: 1 -mode: monitor -session_ttl: 8h - -defaults: - bash: allow - read: allow - write: allow - -gates: - - id: supply-chain.pkg-install - severity: high - match: - tool: Bash - command_regex: '^(pip|pip3|npm|pnpm|yarn|brew|cargo|gem) (install|add|-i)\b' - evaluate: - - kind: typosquat - reference: __INLINE__:numpy,pandas,requests,flask,django,fastapi,pytest,pip,setuptools,wheel,react,vue,express,lodash,axios,typescript,eslint,prettier,jest,vite,git,node,python,go,rust,docker,kubectl - action_on_near_miss: deny - - kind: allowlist - list: __INLINE__:numpy,pandas,requests,flask,django,fastapi,pytest,pip,setuptools,wheel,react,vue,express,lodash,axios,typescript,eslint,prettier,jest,vite,git,node,python,go,rust,docker,kubectl - on_hit: allow - on_miss: deny - - - id: supply-chain.untrusted-mcp - match: - any_of: - - { tool_prefix: "mcp__" } - - { tool: mcp.before_execution } - evaluate: - - kind: pin-tofu - store: $AGENTLOCK_HOME/pinned-mcp.json - on_known: allow - on_unknown: deny - on_changed: deny - - - id: rogue.secret-read - severity: high - match: - any_of: - - { tool: Read, path_glob: "**/.env*" } - - { tool: Read, path_glob: "**/.ssh/**" } - - { tool: Read, path_glob: "**/.aws/**" } - - { tool: Bash, command_regex: '(\.env(\b|[._-])|/\.ssh(/|\b)|/\.aws(/|\b)|credentials)' } - evaluate: - - kind: always - action: deny - - - id: rogue.net-egress - match: - any_of: - - { tool: Bash, command_regex: '\b(curl|wget|nc|netcat)\b' } - - { tool: mcp.http.request } - evaluate: - - kind: host-allowlist - list: __INLINE__:github.com,raw.githubusercontent.com,api.github.com,registry.npmjs.org,pypi.org,files.pythonhosted.org,crates.io,static.crates.io,rubygems.org,pkg.go.dev,proxy.golang.org,sum.golang.org - on_hit: allow - on_miss: deny - - - id: rogue.destructive-bash - severity: critical - match: - tool: Bash - any_command_regex: - - 'rm\s+(-[rRfF]+\s+)+\S+' - - 'git\s+push\s+.*--force' - - 'kubectl\s+delete\s+' - - 'DROP\s+(TABLE|DATABASE|SCHEMA)' - - 'chmod\s+-R\b' - evaluate: - - kind: always - action: deny - require_rotate: true