@b00dea/cockpit 0.0.0-bootstrap.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +375 -0
- package/LICENSE +21 -0
- package/README.md +21 -1
- package/bin/cockpit.mjs +33 -0
- package/cockpit.schema.json +2574 -0
- package/dist/autonomy.d.ts +44 -0
- package/dist/autonomy.js +156 -0
- package/dist/bounded-read.d.ts +14 -0
- package/dist/bounded-read.js +42 -0
- package/dist/browser/commands.d.ts +62 -0
- package/dist/browser/commands.js +349 -0
- package/dist/browser/package-paths.d.ts +1 -0
- package/dist/browser/package-paths.js +6 -0
- package/dist/browser/proxy.d.ts +22 -0
- package/dist/browser/proxy.js +37 -0
- package/dist/browser/runtime.d.ts +117 -0
- package/dist/browser/runtime.js +183 -0
- package/dist/browser/surfaces.d.ts +141 -0
- package/dist/browser/surfaces.js +556 -0
- package/dist/browser/ui-gate.d.ts +15 -0
- package/dist/browser/ui-gate.js +18 -0
- package/dist/cli-guard.d.ts +5 -0
- package/dist/cli-guard.js +71 -0
- package/dist/cli-main.d.ts +139 -0
- package/dist/cli-main.js +1405 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +16 -0
- package/dist/command-table.d.ts +113 -0
- package/dist/command-table.js +512 -0
- package/dist/config/load.d.ts +171 -0
- package/dist/config/load.js +345 -0
- package/dist/config/sibling-toml.d.ts +48 -0
- package/dist/config/sibling-toml.js +160 -0
- package/dist/config/surface-tools-toml.d.ts +9 -0
- package/dist/config/surface-tools-toml.js +37 -0
- package/dist/config/verified-write.d.ts +43 -0
- package/dist/config/verified-write.js +89 -0
- package/dist/diagnostics.d.ts +26 -0
- package/dist/diagnostics.js +335 -0
- package/dist/emit.d.ts +22 -0
- package/dist/emit.js +43 -0
- package/dist/entrypoint-kinds.d.ts +3 -0
- package/dist/entrypoint-kinds.js +11 -0
- package/dist/environment/application/destructive.d.ts +6 -0
- package/dist/environment/application/destructive.js +425 -0
- package/dist/environment/application/doctor.d.ts +89 -0
- package/dist/environment/application/doctor.js +235 -0
- package/dist/environment/application/guard.d.ts +43 -0
- package/dist/environment/application/guard.js +622 -0
- package/dist/environment/application/health.d.ts +134 -0
- package/dist/environment/application/health.js +379 -0
- package/dist/environment/application/lifecycle-context.d.ts +228 -0
- package/dist/environment/application/lifecycle-context.js +154 -0
- package/dist/environment/application/lifecycle-state.d.ts +25 -0
- package/dist/environment/application/lifecycle-state.js +201 -0
- package/dist/environment/application/lifecycle.d.ts +17 -0
- package/dist/environment/application/lifecycle.js +729 -0
- package/dist/environment/application/logs.d.ts +45 -0
- package/dist/environment/application/logs.js +127 -0
- package/dist/environment/application/setup.d.ts +129 -0
- package/dist/environment/application/setup.js +741 -0
- package/dist/environment/application/shared.d.ts +11 -0
- package/dist/environment/application/shared.js +61 -0
- package/dist/environment/claims.d.ts +389 -0
- package/dist/environment/claims.js +1709 -0
- package/dist/environment/codex-hook-trust.d.ts +11 -0
- package/dist/environment/codex-hook-trust.js +185 -0
- package/dist/environment/compose-environment.d.ts +1 -0
- package/dist/environment/compose-environment.js +6 -0
- package/dist/environment/compose-ownership.d.ts +21 -0
- package/dist/environment/compose-ownership.js +117 -0
- package/dist/environment/compose-topology.d.ts +117 -0
- package/dist/environment/compose-topology.js +413 -0
- package/dist/environment/contract.d.ts +489 -0
- package/dist/environment/contract.js +761 -0
- package/dist/environment/declaration.d.ts +14 -0
- package/dist/environment/declaration.js +39 -0
- package/dist/environment/destructive-composition.d.ts +29 -0
- package/dist/environment/destructive-composition.js +333 -0
- package/dist/environment/docker-daemon.d.ts +30 -0
- package/dist/environment/docker-daemon.js +78 -0
- package/dist/environment/docker-endpoint.d.ts +10 -0
- package/dist/environment/docker-endpoint.js +19 -0
- package/dist/environment/env-schema-observation.d.ts +46 -0
- package/dist/environment/env-schema-observation.js +241 -0
- package/dist/environment/evidence.d.ts +136 -0
- package/dist/environment/evidence.js +437 -0
- package/dist/environment/executable.d.ts +26 -0
- package/dist/environment/executable.js +167 -0
- package/dist/environment/generated-application/authorities.d.ts +5 -0
- package/dist/environment/generated-application/authorities.js +61 -0
- package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
- package/dist/environment/generated-application/bootstrap-skill.js +328 -0
- package/dist/environment/generated-application/check.d.ts +13 -0
- package/dist/environment/generated-application/check.js +189 -0
- package/dist/environment/generated-application/custom-surface.d.ts +26 -0
- package/dist/environment/generated-application/custom-surface.js +289 -0
- package/dist/environment/generated-application/prepare.d.ts +29 -0
- package/dist/environment/generated-application/prepare.js +276 -0
- package/dist/environment/generated-application/publication.d.ts +33 -0
- package/dist/environment/generated-application/publication.js +547 -0
- package/dist/environment/generated-application/shared.d.ts +484 -0
- package/dist/environment/generated-application/shared.js +349 -0
- package/dist/environment/generated-application/surface-observation.d.ts +4 -0
- package/dist/environment/generated-application/surface-observation.js +69 -0
- package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
- package/dist/environment/generated-application/surface-outputs.js +389 -0
- package/dist/environment/generated-application/surfaces.d.ts +3 -0
- package/dist/environment/generated-application/surfaces.js +699 -0
- package/dist/environment/generated-application/sync.d.ts +6 -0
- package/dist/environment/generated-application/sync.js +109 -0
- package/dist/environment/git-observation.d.ts +183 -0
- package/dist/environment/git-observation.js +544 -0
- package/dist/environment/health-composition.d.ts +11 -0
- package/dist/environment/health-composition.js +628 -0
- package/dist/environment/host-observation.d.ts +56 -0
- package/dist/environment/host-observation.js +52 -0
- package/dist/environment/identity.d.ts +48 -0
- package/dist/environment/identity.js +85 -0
- package/dist/environment/init-application.d.ts +308 -0
- package/dist/environment/init-application.js +697 -0
- package/dist/environment/install-policy-observation.d.ts +71 -0
- package/dist/environment/install-policy-observation.js +284 -0
- package/dist/environment/install-policy.d.ts +48 -0
- package/dist/environment/install-policy.js +96 -0
- package/dist/environment/instance.d.ts +33 -0
- package/dist/environment/instance.js +122 -0
- package/dist/environment/instruction-pointer.d.ts +19 -0
- package/dist/environment/instruction-pointer.js +144 -0
- package/dist/environment/lifecycle-composition.d.ts +29 -0
- package/dist/environment/lifecycle-composition.js +675 -0
- package/dist/environment/lifecycle-stage.d.ts +115 -0
- package/dist/environment/lifecycle-stage.js +398 -0
- package/dist/environment/lifecycle.d.ts +75 -0
- package/dist/environment/lifecycle.js +354 -0
- package/dist/environment/logs.d.ts +132 -0
- package/dist/environment/logs.js +519 -0
- package/dist/environment/observations.d.ts +46 -0
- package/dist/environment/observations.js +344 -0
- package/dist/environment/operator-application.d.ts +50 -0
- package/dist/environment/operator-application.js +155 -0
- package/dist/environment/ports.d.ts +182 -0
- package/dist/environment/ports.js +1205 -0
- package/dist/environment/posture-observation.d.ts +68 -0
- package/dist/environment/posture-observation.js +253 -0
- package/dist/environment/read-composition.d.ts +27 -0
- package/dist/environment/read-composition.js +352 -0
- package/dist/environment/readiness-composition.d.ts +38 -0
- package/dist/environment/readiness-composition.js +130 -0
- package/dist/environment/reclaim.d.ts +333 -0
- package/dist/environment/reclaim.js +1835 -0
- package/dist/environment/repository-identity.d.ts +13 -0
- package/dist/environment/repository-identity.js +108 -0
- package/dist/environment/runtime-measurement-record.d.ts +38 -0
- package/dist/environment/runtime-measurement-record.js +111 -0
- package/dist/environment/runtime-measurement.d.ts +27 -0
- package/dist/environment/runtime-measurement.js +224 -0
- package/dist/environment/runtime-probe-residue.d.ts +43 -0
- package/dist/environment/runtime-probe-residue.js +148 -0
- package/dist/environment/runtime-probe.d.ts +72 -0
- package/dist/environment/runtime-probe.js +725 -0
- package/dist/environment/runtime.d.ts +505 -0
- package/dist/environment/runtime.js +2335 -0
- package/dist/environment/seed.d.ts +16 -0
- package/dist/environment/seed.js +43 -0
- package/dist/environment/selftest-application.d.ts +86 -0
- package/dist/environment/selftest-application.js +713 -0
- package/dist/environment/sensitive.d.ts +3 -0
- package/dist/environment/sensitive.js +48 -0
- package/dist/environment/setup-observation.d.ts +62 -0
- package/dist/environment/setup-observation.js +1473 -0
- package/dist/environment/setup.d.ts +43 -0
- package/dist/environment/setup.js +176 -0
- package/dist/environment/sibling-instructions.d.ts +33 -0
- package/dist/environment/sibling-instructions.js +224 -0
- package/dist/environment/sibling-registration.d.ts +45 -0
- package/dist/environment/sibling-registration.js +538 -0
- package/dist/environment/state.d.ts +371 -0
- package/dist/environment/state.js +2013 -0
- package/dist/environment/templates.d.ts +140 -0
- package/dist/environment/templates.js +685 -0
- package/dist/environment/tool-registration.d.ts +9 -0
- package/dist/environment/tool-registration.js +96 -0
- package/dist/environment/upgrade-application.d.ts +19 -0
- package/dist/environment/upgrade-application.js +175 -0
- package/dist/environment-exec/execute.d.ts +102 -0
- package/dist/environment-exec/execute.js +1175 -0
- package/dist/environment-exec/libexec-program.d.ts +6 -0
- package/dist/environment-exec/libexec-program.js +22 -0
- package/dist/environment-exec/verified-digest.d.ts +4 -0
- package/dist/environment-exec/verified-digest.js +16 -0
- package/dist/exact-keys.d.ts +7 -0
- package/dist/exact-keys.js +15 -0
- package/dist/fix-catalogue.d.ts +32 -0
- package/dist/fix-catalogue.js +60 -0
- package/dist/freshness.d.ts +29 -0
- package/dist/freshness.js +75 -0
- package/dist/generated-environment/adopt.d.ts +16 -0
- package/dist/generated-environment/adopt.js +275 -0
- package/dist/generated-environment/command.d.ts +47 -0
- package/dist/generated-environment/command.js +75 -0
- package/dist/generated-environment/transaction.d.ts +186 -0
- package/dist/generated-environment/transaction.js +1770 -0
- package/dist/grading/evaluate.d.ts +71 -0
- package/dist/grading/evaluate.js +403 -0
- package/dist/grading/grade.d.ts +33 -0
- package/dist/grading/grade.js +57 -0
- package/dist/guards/evaluator.d.ts +113 -0
- package/dist/guards/evaluator.js +1226 -0
- package/dist/guards/harness-adapter.d.ts +17 -0
- package/dist/guards/harness-adapter.js +57 -0
- package/dist/guards/host.d.ts +8 -0
- package/dist/guards/host.js +10 -0
- package/dist/guards/invocation-parser.d.ts +66 -0
- package/dist/guards/invocation-parser.js +595 -0
- package/dist/guards/local-identity.d.ts +40 -0
- package/dist/guards/local-identity.js +123 -0
- package/dist/guards/policy-pin.d.ts +13 -0
- package/dist/guards/policy-pin.js +175 -0
- package/dist/guards/protected-locations.d.ts +62 -0
- package/dist/guards/protected-locations.js +461 -0
- package/dist/guards/read-only.d.ts +37 -0
- package/dist/guards/read-only.js +315 -0
- package/dist/init/command.d.ts +40 -0
- package/dist/init/command.js +310 -0
- package/dist/init/derive.d.ts +51 -0
- package/dist/init/derive.js +715 -0
- package/dist/init/draft.d.ts +138 -0
- package/dist/init/draft.js +1205 -0
- package/dist/init/render.d.ts +35 -0
- package/dist/init/render.js +82 -0
- package/dist/init/scaffold.d.ts +53 -0
- package/dist/init/scaffold.js +121 -0
- package/dist/invocation.d.ts +13 -0
- package/dist/invocation.js +20 -0
- package/dist/package-metadata.d.ts +9 -0
- package/dist/package-metadata.js +19 -0
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.js +88 -0
- package/dist/references/named.d.ts +24 -0
- package/dist/references/named.js +27 -0
- package/dist/refusal.d.ts +15 -0
- package/dist/refusal.js +7 -0
- package/dist/registry-derivation.d.ts +31 -0
- package/dist/registry-derivation.js +32 -0
- package/dist/registry-resolve/command.d.ts +47 -0
- package/dist/registry-resolve/command.js +226 -0
- package/dist/registry-resolve/registry.d.ts +68 -0
- package/dist/registry-resolve/registry.js +292 -0
- package/dist/registry-resolve/render-shell.d.ts +18 -0
- package/dist/registry-resolve/render-shell.js +36 -0
- package/dist/repair.d.ts +29 -0
- package/dist/repair.js +8 -0
- package/dist/schema-json.d.ts +1 -0
- package/dist/schema-json.js +177 -0
- package/dist/schema.d.ts +1322 -0
- package/dist/schema.js +934 -0
- package/dist/setup/command.d.ts +4 -0
- package/dist/setup/command.js +4 -0
- package/dist/sha256.d.ts +1 -0
- package/dist/sha256.js +4 -0
- package/dist/surfaces/adoption.d.ts +41 -0
- package/dist/surfaces/adoption.js +217 -0
- package/dist/surfaces/consumer-instructions.d.ts +20 -0
- package/dist/surfaces/consumer-instructions.js +43 -0
- package/dist/surfaces/contract.d.ts +42 -0
- package/dist/surfaces/contract.js +169 -0
- package/dist/surfaces/core-declarations.d.ts +24 -0
- package/dist/surfaces/core-declarations.js +79 -0
- package/dist/surfaces/development-markers.d.ts +1 -0
- package/dist/surfaces/development-markers.js +18 -0
- package/dist/surfaces/generator-adapter.d.ts +71 -0
- package/dist/surfaces/generator-adapter.js +809 -0
- package/dist/surfaces/harness-registry.d.ts +180 -0
- package/dist/surfaces/harness-registry.js +117 -0
- package/dist/surfaces/mcp.d.ts +95 -0
- package/dist/surfaces/mcp.js +186 -0
- package/dist/surfaces/merge.d.ts +30 -0
- package/dist/surfaces/merge.js +105 -0
- package/dist/surfaces/ownership.d.ts +190 -0
- package/dist/surfaces/ownership.js +795 -0
- package/dist/surfaces/projection.d.ts +136 -0
- package/dist/surfaces/projection.js +681 -0
- package/dist/surfaces/render.d.ts +118 -0
- package/dist/surfaces/render.js +633 -0
- package/dist/surfaces/restrictions.d.ts +130 -0
- package/dist/surfaces/restrictions.js +447 -0
- package/dist/surfaces/selftest.d.ts +162 -0
- package/dist/surfaces/selftest.js +393 -0
- package/dist/surfaces/skill-contract.d.ts +85 -0
- package/dist/surfaces/skill-contract.js +449 -0
- package/dist/surfaces/sync.d.ts +57 -0
- package/dist/surfaces/sync.js +164 -0
- package/dist/team-metadata.d.ts +41 -0
- package/dist/team-metadata.js +512 -0
- package/dist/testing/gates.d.ts +91 -0
- package/dist/testing/gates.js +182 -0
- package/dist/testing/hooks.d.ts +59 -0
- package/dist/testing/hooks.js +139 -0
- package/dist/testing/verdict.d.ts +29 -0
- package/dist/testing/verdict.js +62 -0
- package/dist/work-orders/application.d.ts +99 -0
- package/dist/work-orders/application.js +383 -0
- package/dist/work-orders/composition.d.ts +124 -0
- package/dist/work-orders/composition.js +1010 -0
- package/dist/work-orders/contract.d.ts +61 -0
- package/dist/work-orders/contract.js +122 -0
- package/dist/work-orders/store.d.ts +158 -0
- package/dist/work-orders/store.js +197 -0
- package/docs/README.md +65 -0
- package/docs/architecture.md +136 -0
- package/docs/capability-matrix.md +50 -0
- package/docs/cli-reference.md +599 -0
- package/docs/command-list.md +51 -0
- package/docs/compose-patterns.md +99 -0
- package/docs/concepts.md +171 -0
- package/docs/configuration-essentials.md +172 -0
- package/docs/configuration-reference.md +621 -0
- package/docs/environment.md +760 -0
- package/docs/extending.md +43 -0
- package/docs/faq.md +111 -0
- package/docs/getting-started.md +608 -0
- package/docs/harness-surfaces.md +433 -0
- package/docs/joining-a-team.md +76 -0
- package/docs/release-policy.md +49 -0
- package/docs/safety-and-autonomy.md +419 -0
- package/docs/troubleshooting.md +361 -0
- package/docs/tutorial.md +170 -0
- package/docs/work-and-browser.md +254 -0
- package/libexec/browser-operation.cjs +186 -0
- package/libexec/fence-marker.cjs +101 -0
- package/libexec/fence-marker.d.cts +24 -0
- package/libexec/fenced-group-runner.cjs +77 -0
- package/libexec/fenced-supervisor.cjs +137 -0
- package/libexec/generated-filesystem.cjs +731 -0
- package/libexec/install-filesystem.cjs +225 -0
- package/libexec/rulesync-generator.NOTICE.md +1112 -0
- package/libexec/rulesync-generator.cjs +743 -0
- package/libexec/surface-generator-worker.cjs +266 -0
- package/libexec/surface-output-reader.cjs +99 -0
- package/libexec/surface-scratch-cleanup.cjs +14 -0
- package/libexec/validate-harness-evidence.d.mts +183 -0
- package/libexec/validate-harness-evidence.mjs +1312 -0
- package/package.json +76 -1
- package/surfaces/README.md +33 -0
- package/surfaces/agents/evaluator.agent.md +76 -0
- package/surfaces/guards/bash-guard.sh +48 -0
- package/surfaces/guards/claude.adapter.json +25 -0
- package/surfaces/guards/codex.adapter.json +24 -0
- package/surfaces/guards/opencode.adapter.json +25 -0
- package/surfaces/references/browser.md +63 -0
- package/surfaces/references/test-data-and-personas.md +84 -0
- package/surfaces/references/testing-tiers.md +11 -0
- package/surfaces/skills/cockpit-browser.skill.md +54 -0
- package/surfaces/skills/cockpit-explain.skill.md +32 -0
- package/surfaces/skills/cockpit-fix.skill.md +37 -0
- package/surfaces/skills/cockpit-setup.skill.md +39 -0
- package/surfaces/skills/cockpit-work.skill.md +34 -0
- package/surfaces/skills/collection.json +5 -0
- package/surfaces/skills/procedures/explain.md +61 -0
- package/surfaces/skills/procedures/fix.md +81 -0
- package/surfaces/skills/procedures/setup.md +379 -0
- package/surfaces/skills/procedures/work.md +105 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# Safety and autonomy
|
|
2
|
+
|
|
3
|
+
This page is for people reviewing the controls cockpit generates and for agents working within
|
|
4
|
+
them. It says what the guard allows, asks about, denies or cannot evaluate, and why. A permission
|
|
5
|
+
always applies to one exact command and declared resources, never a whole command family.
|
|
6
|
+
|
|
7
|
+
The [command table](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/command-table.ts) and
|
|
8
|
+
[guard evaluator](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/guards/evaluator.ts) own these decisions; the generated
|
|
9
|
+
[CLI reference](cli-reference.md), [command list](command-list.md) and
|
|
10
|
+
[configuration reference](configuration-reference.md) describe their public inputs.
|
|
11
|
+
|
|
12
|
+
## Outcome vocabulary
|
|
13
|
+
|
|
14
|
+
| Outcome | Meaning |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `allow` | Evaluation completed without a harder boundary; executable invocations match exact permitted profiles or the built-in read-only profile. |
|
|
17
|
+
| `ask` | A person must decide. An `ask` result permits nothing by itself. |
|
|
18
|
+
| `deny` | A hard boundary forbids the request, even with an exact grant. |
|
|
19
|
+
| `unavailable` | Cockpit could not verify or safely evaluate the request, so it permits nothing. |
|
|
20
|
+
|
|
21
|
+
The evaluator scans the whole captured command for hard denials before returning `unavailable` or
|
|
22
|
+
`ask`, so a softer result found earlier cannot hide a later forbidden operation. Malformed input or
|
|
23
|
+
an incomplete policy is `unavailable` before that scan can begin.
|
|
24
|
+
|
|
25
|
+
## Cockpit and project commands
|
|
26
|
+
|
|
27
|
+
A cockpit command is allowed only when it matches an exact available public command profile. The
|
|
28
|
+
application verifies the executable and working root; the command resolver checks the bounded
|
|
29
|
+
complete argument vector, resolved configuration path, declared services, permitted option roles
|
|
30
|
+
and inspected paths. Internal guard transport and public commands marked unavailable are excluded.
|
|
31
|
+
Declared project build and test commands are allowed only as their exact argument vectors in their
|
|
32
|
+
own checkout. A wildcard over a command family never grants permission.
|
|
33
|
+
|
|
34
|
+
## Hard controls and prompts
|
|
35
|
+
|
|
36
|
+
Evaluation runs in this order: built-in hard denials, declared denials, checks cockpit could not
|
|
37
|
+
complete, declared allows, the built-in read-only profile, then `ask`. A declared allow therefore
|
|
38
|
+
cannot bypass any boundary below.
|
|
39
|
+
|
|
40
|
+
### Instances, secrets and hosted endpoints
|
|
41
|
+
|
|
42
|
+
The guard denies mutation or staging of a declared environment instance, and mutation below a
|
|
43
|
+
secrets directory. That covers command and patch removals as well as writes, removing a directory
|
|
44
|
+
that contains an instance or the secrets directory, and staging a containing directory that could
|
|
45
|
+
include an instance.
|
|
46
|
+
|
|
47
|
+
Every `.env` and `.env.*` file at any depth is treated as holding secrets, declared or not: reading,
|
|
48
|
+
writing, removing, staging or linking one is a hard denial through every guarded tool. The
|
|
49
|
+
conventional documentation names `.env.example`, `.env.sample`, `.env.template` and `.env.schema`
|
|
50
|
+
are exempt, and so is every declared environment `template`. The refusal says what to do instead:
|
|
51
|
+
run what needs the values as `varlock run -- <cmd>`, which never prints them, or read the variable
|
|
52
|
+
names from the `.env.schema`. Another name that starts with `.env`, such as `.envrc`, is not denied,
|
|
53
|
+
but reading it prompts.
|
|
54
|
+
|
|
55
|
+
Executable use of a declared hosted database or vendor host is denied at hostname boundaries.
|
|
56
|
+
Matching examines executable, argument and environment-assignment-value roles; redirects,
|
|
57
|
+
heredocs, comments and definitions do not count as executable host use. Host names are compared
|
|
58
|
+
after Unicode compatibility normalization, so fullwidth letters and ideographic full stops, which
|
|
59
|
+
resolvers fold to ASCII, do not hide a declared host.
|
|
60
|
+
|
|
61
|
+
### Configuration, Git and guard controls
|
|
62
|
+
|
|
63
|
+
Every guarded call reads the configuration file and
|
|
64
|
+
`.cockpit/generated-environment-ownership.json`, so losing either holds the agent until a human
|
|
65
|
+
restores it. Deleting, renaming, truncating, relinking or making either unreadable is a hard
|
|
66
|
+
denial, and so is removing a directory that contains either. The guard recognises this through
|
|
67
|
+
`rm`, `unlink`, `mv`, `git rm`, `git mv`, `find -delete`, `truncate`, `git archive` output,
|
|
68
|
+
truncating `dd` or `tee` writes, `ln` onto either file, a `chmod` mode that drops the owner's read
|
|
69
|
+
permission or is copied with `--reference`, a patch delete or move, a Write tool call with empty
|
|
70
|
+
content, and a `>` redirect. Editing the configuration in place stays permitted, including bounded
|
|
71
|
+
`dd conv=notrunc` and `tee` append forms under the normal write checks; the edit takes effect only
|
|
72
|
+
after a human runs `sync`.
|
|
73
|
+
|
|
74
|
+
Writing anywhere under a `.git` directory is a hard denial, because Git configuration chooses
|
|
75
|
+
programs that read-only Git commands run. For the same reason, so is every `git config` form other
|
|
76
|
+
than a proven read (`--get`, `--get-all`, `--get-regexp`, `--list`, `get`, `list`, or a lone
|
|
77
|
+
`section.key`), whatever its scope: a global `core.pager` or `core.hooksPath` reaches every
|
|
78
|
+
repository.
|
|
79
|
+
|
|
80
|
+
Removing, renaming or directly overwriting a guard control is a hard denial, as is removing a
|
|
81
|
+
directory that contains one or an executable control below a protected plugin directory. Otherwise
|
|
82
|
+
an exact command allow could disable a hook, or change a program a harness starts, before the next
|
|
83
|
+
guarded call. The protected files include every harness's native MCP and permission outputs derived
|
|
84
|
+
from the shipped harness registry, the MCP wrapper scripts under `.cockpit/mcp/` that those
|
|
85
|
+
outputs start, the work-order records under `.cockpit/work-orders/`, which carry no key, so one an
|
|
86
|
+
agent wrote would read as a person's to the next `cockpit work` command, and `.cockpit/policy.json`,
|
|
87
|
+
the [autonomy policy](#who-decides) the last sync accepted. This boundary follows folded path
|
|
88
|
+
spellings and verified filesystem identities, so alternate case and aliases do not bypass it.
|
|
89
|
+
|
|
90
|
+
The installed code the guard runs is a control too. The guard checks its entry point's digest, but
|
|
91
|
+
an edited module it loads could turn every hard denial into an allow. So these are hard denials:
|
|
92
|
+
|
|
93
|
+
- writing, linking, changing the mode of, or removing anything inside the running cockpit package
|
|
94
|
+
or any package it loads, under both its installed path and its real path, or removing a
|
|
95
|
+
directory that contains one;
|
|
96
|
+
- writing into any `node_modules/.bin` directory of a configured root, where package scripts and
|
|
97
|
+
`npx` find their commands;
|
|
98
|
+
- creating, replacing or linking a file directly inside a directory on the harness's command
|
|
99
|
+
search path (`PATH`) that lies within a configured root, where a planted `ls`, `git` or `rg`
|
|
100
|
+
would run in place of a permitted read-only command.
|
|
101
|
+
|
|
102
|
+
Only absolute search path entries can be protected: a relative entry such as `.`, `./node_modules/.bin`
|
|
103
|
+
or an empty one names whatever directory a command runs in, so a file the agent wrote anywhere could
|
|
104
|
+
run in place of a permitted command, or as the interpreter a path-spelled script such as
|
|
105
|
+
`./node_modules/.bin/cockpit` starts through `#!/usr/bin/env node`. While the harness's `PATH` holds
|
|
106
|
+
one, no command is allowed without a prompt, whatever profile or declared rule matches it, and the
|
|
107
|
+
reason names the entry. Keep relative entries off the `PATH` an agent harness starts with; `doctor`
|
|
108
|
+
reports one in the `PATH` it runs with as `GUARD_SEARCH_PATH_RELATIVE`.
|
|
109
|
+
|
|
110
|
+
The Claude Code registration includes `NotebookEdit`, so notebook cell changes get the same
|
|
111
|
+
protected-path checks as text edits. For a configuration in a repository subdirectory, these
|
|
112
|
+
controls belong to the configuration's directory. Start Codex in that directory: its generated hook
|
|
113
|
+
resolves the guard from the canonical session directory, not from an enclosing Git repository.
|
|
114
|
+
Generating a hook does not establish that the harness runs it; see
|
|
115
|
+
[Capability states](harness-surfaces.md#capability-states).
|
|
116
|
+
|
|
117
|
+
### Destinations, sources and links
|
|
118
|
+
|
|
119
|
+
The evaluator resolves visible destinations for bounded `cp`, `install`, `mv`, `ln`, `git mv`,
|
|
120
|
+
`git archive`, `tee`, `dd`, `touch`, `truncate` and `chmod` forms, and the destination of bounded
|
|
121
|
+
`tar` extraction forms. Git paths use the directory selected by `git -C`. Extraction without an
|
|
122
|
+
explicit destination writes to the invocation's working directory. Visible source files for bounded
|
|
123
|
+
`cp`, `install`, `dd` and `tar` forms get the same protected read check as built-in read-only
|
|
124
|
+
commands.
|
|
125
|
+
|
|
126
|
+
A hard or symbolic `ln` whose target is, contains or lies inside a secrets directory, a declared
|
|
127
|
+
instance, a guard control or the installed cockpit is a hard denial, because the link would reach
|
|
128
|
+
it under a name the guard does not protect. A symbolic link's target is checked from both the
|
|
129
|
+
working directory and the link's own directory.
|
|
130
|
+
|
|
131
|
+
These are `unavailable`: every unsupported `tar` or `ln` mode or option, and a command whose
|
|
132
|
+
visible operand names a control, a protected plugin subtree or a directory containing either
|
|
133
|
+
and that the guard cannot classify. These checks run before declared allows. They do not inspect archive
|
|
134
|
+
member paths or the bodies of approved scripts, and classifying an extraction directory does not
|
|
135
|
+
prove that an archive cannot traverse outside it. An exact command rule permits that command; it is
|
|
136
|
+
not a filesystem sandbox. To restore or intentionally replace generated controls, stop the guarded
|
|
137
|
+
session and have a human run `cockpit sync --config cockpit.toml` in a terminal.
|
|
138
|
+
|
|
139
|
+
### Protected branches and prompts
|
|
140
|
+
|
|
141
|
+
Pushes to declared protected branches are hard denials, including explicit protected destinations
|
|
142
|
+
and `git push --all` or `git push --mirror` when protected branches are declared. Missing or
|
|
143
|
+
unresolved destinations, including ambiguous source-only revisions, symbolic aliases and wildcards,
|
|
144
|
+
need a human decision. An exact grant cannot override either boundary.
|
|
145
|
+
|
|
146
|
+
These produce `ask`: unresolved Git execution context, destructive Git reset or clean forms,
|
|
147
|
+
staging by explicit path (a person approves or runs it), staging without explicit paths, wrapped or environment-modifying commands, unmatched invocation
|
|
148
|
+
profiles, and any removal without a grant. An explicit removal outside configured roots stays at
|
|
149
|
+
`ask` even when a declared allow matches, because cockpit cannot verify a target outside the roots
|
|
150
|
+
it manages; review the exact path and approve it, or move the work under a configured root. `git add -A`, `git add .` or a
|
|
151
|
+
directory operand that covers a secrets directory or an environment instance is a hard denial:
|
|
152
|
+
stage the changed files by name. Unsupported staging options or unproven pathspec grammar produce
|
|
153
|
+
`unavailable`. Any other refused command gets an `ask` that names the command and gives three ways
|
|
154
|
+
forward: a person approves it, a person runs it in a terminal, or a person admits it under
|
|
155
|
+
`[guards] allow`; for tests it points to `cockpit test-run`.
|
|
156
|
+
|
|
157
|
+
### Declared rules
|
|
158
|
+
|
|
159
|
+
Declared rules refine the final prompt boundary. Each vector starts with the executable.
|
|
160
|
+
`[[guards.deny]]` matches a command-vector prefix or a contiguous command-vector sequence and may
|
|
161
|
+
name a safe fix. `[[guards.allow]]` matches a command-vector prefix, optionally only at one
|
|
162
|
+
checkout-relative working directory. An allow cannot bypass a hosted endpoint, protected branch,
|
|
163
|
+
secret path, malformed capture or unavailable filesystem evidence. Wrapped and
|
|
164
|
+
environment-modifying invocations are never allowed by a declared rule, because wrappers can change
|
|
165
|
+
which program actually runs. A rule that admits an interpreter, such as `["node"]`, `["python3"]` or
|
|
166
|
+
`["perl"]`, admits whatever code it is given, inline (`node -e`) or in a file, and the guard cannot
|
|
167
|
+
see what that code writes, guard controls included; without such a rule those invocations ask.
|
|
168
|
+
|
|
169
|
+
Malformed or oversized input, incomplete policy, escaped or symlinked path evidence, inspection
|
|
170
|
+
failure and exceeded limits produce `unavailable`. The
|
|
171
|
+
[invocation parser](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/guards/invocation-parser.ts) captures bounded input
|
|
172
|
+
once; the evaluator accepts only input that parser produced, so a hand-built lookalike is refused.
|
|
173
|
+
|
|
174
|
+
### Read-only commands
|
|
175
|
+
|
|
176
|
+
A built-in read-only profile lets agents inspect the checkout without a prompt, which matters most
|
|
177
|
+
on Codex and OpenCode, where a prompt becomes a denial. It covers exact argument shapes of `ls`,
|
|
178
|
+
`pwd`, `cat`, `head`, `tail`, `wc`, `rg`, `grep`, `fd`, `find`, `echo`, `printf`, `sed`,
|
|
179
|
+
`git status`, `git diff`, `git log`, `git show`, `git rev-parse`, `git branch` listing flags, and the
|
|
180
|
+
cockpit commands in
|
|
181
|
+
[Cockpit commands an agent can run](#cockpit-commands-an-agent-can-run). The
|
|
182
|
+
[read-only profile](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/guards/read-only.ts) lists each permitted option.
|
|
183
|
+
|
|
184
|
+
- Any other option falls back to the ordinary rules, including options that run programs or write
|
|
185
|
+
files: `rg --pre`, `find -exec`, `find -delete`, `fd --exec`, `git -c`, `git --output`,
|
|
186
|
+
`git --ext-diff` and `git diff --no-index`.
|
|
187
|
+
- Options whose value is optional, such as `grep --context` on macOS, are accepted only in their
|
|
188
|
+
attached `--option=value` form, so the next word can never be misread.
|
|
189
|
+
- `printf` is read-only only without options, because `printf -v` assigns a variable. `sed` only
|
|
190
|
+
prints: `sed -n '5p'` or `sed -n '1,$p'` with numeric or `$` addresses and the `p` command, since
|
|
191
|
+
other commands can write or run programs. `git rev-parse` is read-only except `--git-path`,
|
|
192
|
+
`--resolve-git-dir`, `--parseopt` and `--sq-quote`. `git branch` is read-only with no argument or
|
|
193
|
+
with `--list`, `-a`, `-r`, `-v`, `-vv` or `--show-current` (and their long forms), never with a
|
|
194
|
+
branch name.
|
|
195
|
+
- `$?` is allowed as an argument only to a read-only command, where it cannot name a path, and
|
|
196
|
+
never as the directory of `env -C`.
|
|
197
|
+
- A command may follow `cd ./dir`, `cd ../dir` or `cd /abs/dir` joined by `&&` or `;`, with the
|
|
198
|
+
directory then judged as the command's working directory. A bare `cd name` is refused, because
|
|
199
|
+
`CDPATH` can send it elsewhere the guard cannot see: write `cd ./name`, or use `git -C name` for
|
|
200
|
+
Git. A `..` after a directory name, as in `cd ./a/../b` or `env -C a/..`, is refused too: `env`
|
|
201
|
+
resolves it from wherever a link named `a` leads, and so does a shell set to follow links.
|
|
202
|
+
`pushd`, `popd` and zsh's `chdir` are refused, because only `cd` is followed. A `cd`
|
|
203
|
+
may fail, and one after `||` may not run at all, so each later command is judged from every
|
|
204
|
+
directory it may run in: `&&` and `||` group left to right, so in `pwd || cd ./dir && cat x` the
|
|
205
|
+
`cat` is judged from both directories. A newline after `&&`, `||` or `|` continues the list. A
|
|
206
|
+
`cd` in a pipeline is refused, and a list ending in `&` runs in a subshell, so its `cd` moves no
|
|
207
|
+
later command.
|
|
208
|
+
- Git runs read-only only in a directory with its own `.git`, because Git otherwise treats a
|
|
209
|
+
directory holding `HEAD`, `objects` and `refs` as a repository and honours its configuration.
|
|
210
|
+
Git operands are inspected as files whose content may be printed.
|
|
211
|
+
- `rg` is read-only only when `RIPGREP_CONFIG_PATH` is absent, because an ambient ripgrep
|
|
212
|
+
configuration can inject options such as a preprocessor. Its `-g`, `--glob` and `--iglob` are
|
|
213
|
+
read-only only as `!` exclusions: any other glob overrides the hidden and ignore filters, so
|
|
214
|
+
`rg -g '.env*'` would print a hidden environment file.
|
|
215
|
+
- A pipeline or chain is permitted only when every segment is. Redirecting output to `/dev/null`,
|
|
216
|
+
`/dev/stdout` or `/dev/stderr`, or input from `/dev/null`, touches no file and does not change the
|
|
217
|
+
decision.
|
|
218
|
+
|
|
219
|
+
### Cockpit commands an agent can run
|
|
220
|
+
|
|
221
|
+
| Command | Decision | Why |
|
|
222
|
+
| --- | --- | --- |
|
|
223
|
+
| `npx --no cockpit check --config cockpit.toml`, and likewise `doctor`, `preflight`, `logs --service <service>` and `sync --dry-run` | allowed | They read and report; nothing they do changes a file the guard relies on. |
|
|
224
|
+
| `npx --no -- cockpit --help`, `npx --no cockpit up --help`, and the version | allowed | They print and exit before any command runs. |
|
|
225
|
+
| `npx --no cockpit up --config cockpit.toml`, and likewise `sleep` and `down` | allowed | They act only on the stack cockpit declared and owns; `down` keeps data volumes, and deletes only from the local Docker daemon it fixes at the start of the run. |
|
|
226
|
+
| `npx --no cockpit runtime measure --config cockpit.toml --image <local-image>` | allowed | The probe runs a disposable stack under its own project, removes it, and records the result only in this machine's git-ignored state; `cockpit.toml` never changes. |
|
|
227
|
+
| `npx --no cockpit session --config cockpit.toml -- <command>` | the decision for `<command>` | A session runs its command as the agent could run it directly, so it never admits more. |
|
|
228
|
+
| `npx --no cockpit test-run --config cockpit.toml --gate <gate>`, with an optional `--mode` and a `--select <kind>:<value>` whose value does not start with `-` | allowed for a gate `cockpit.toml` declares | The gate runs only the stages a person declared in the reviewed configuration, the trust `up` already has. |
|
|
229
|
+
| `npx --no cockpit work status --config cockpit.toml`, `work accept`, `work check`, `work note`, `work answer`, `work grade`, `work signoff`, `work recover` without `--apply`, `work verify` and `cockpit grade --criteria <file>`, each with its own options | allowed | They report, or take a step [`[work.gates]`](#who-decides) gives the agent; a step it gives a person waits for the digest typed after `--interactive`, which no agent invocation carries. Grading runs in a separate evaluator. |
|
|
230
|
+
| `npx --no cockpit sync --config cockpit.toml`, and `upgrade`, whose sync child writes | allowed where `[autonomy] sync = "agent"`, otherwise a prompt | It regenerates the files the configuration already declares; the guard holds every call after `cockpit.toml` changes until a person syncs, so an agent's sync never publishes an edit of its own. |
|
|
231
|
+
| `npx --no cockpit reset --config cockpit.toml --apply` | allowed where `[autonomy] reset = "agent"`, otherwise a prompt | It deletes stack data. |
|
|
232
|
+
| `npx --no cockpit browser drive --config cockpit.toml`, and likewise `screenshot`, with an optional `--role` | allowed where `browser.request_preapproval` is set, otherwise a prompt | They sign in and capture the declared application, so they run unprompted only on the isolated, non-production data the configuration must declare before it can set preapproval. |
|
|
233
|
+
| `work accept`, `work answer` and `work signoff` with `--interactive` | a prompt, which Codex and OpenCode turn into a denial | `--interactive` asks for a person at a terminal to type the digest of what it shows. |
|
|
234
|
+
| `work recover --apply` | a prompt, which Codex and OpenCode turn into a denial | It deletes a lock once a process probe calls its holder dead, and that probe sees only this machine's processes: a holder in a container or on another host that shares the checkout looks dead to it. |
|
|
235
|
+
| `sync --adopt`, `sync --interactive`, `reset --interactive`, `sibling add` and every other command | a prompt, which Codex and OpenCode turn into a denial | They rewrite `cockpit.toml` or a team's own settings, ask for a person, or delete data. |
|
|
236
|
+
|
|
237
|
+
A file such a command names, like `--criteria`, is checked as a read:
|
|
238
|
+
the command may quote it in a refusal. A declared instance, a file below `.secrets` or a `.env` or
|
|
239
|
+
`.env.*` file that is not documentation is denied, and a symlink or a path outside the configured
|
|
240
|
+
roots prompts.
|
|
241
|
+
|
|
242
|
+
"Allowed" means the guard does not object. Claude Code also needs its own allow rule, which the
|
|
243
|
+
generated settings carry for the commands the shipped skills use
|
|
244
|
+
([Claude Code command rules](harness-surfaces.md#claude-code-command-rules)); in Codex's sandbox
|
|
245
|
+
most of these commands must run outside it
|
|
246
|
+
([Run cockpit outside the Codex sandbox](harness-surfaces.md#run-cockpit-outside-the-codex-sandbox)).
|
|
247
|
+
|
|
248
|
+
Run these exactly as shown. A prefix such as `env -u NAME` or `nohup` makes any of them a wrapped
|
|
249
|
+
invocation, which prompts, and none needs one: `down`, `reset` and `runtime measure` choose their
|
|
250
|
+
Docker daemon themselves, as [Choose the Docker daemon](environment.md#choose-the-docker-daemon)
|
|
251
|
+
describes.
|
|
252
|
+
|
|
253
|
+
These count from the cockpit root with `--config` naming the configured `cockpit.toml`; `check`,
|
|
254
|
+
`doctor` and help also count from any directory. Each spelling counts only where the guard proves
|
|
255
|
+
it starts its own installed package:
|
|
256
|
+
|
|
257
|
+
- a path such as `./node_modules/.bin/cockpit`, or one given to `node`, must resolve to it;
|
|
258
|
+
- bare `cockpit` must be what the command search path finds first;
|
|
259
|
+
- the npx spellings in the table must run from the cockpit root with no root `.npmrc` and no `bin`
|
|
260
|
+
or `directories.bin` in the root `package.json`, because npm would otherwise run that program or
|
|
261
|
+
apply that configuration first. Writing an `.npmrc` anywhere inside the configured roots is
|
|
262
|
+
denied, so one cannot be planted in the same command after that check. Any other npx option, or
|
|
263
|
+
npx without `--no` (which may download a package), is not the installed cockpit.
|
|
264
|
+
|
|
265
|
+
Where npx cannot be proven, every npx spelling prompts, and Codex and OpenCode refuse it, while the
|
|
266
|
+
same command spelled as a path, such as `./node_modules/.bin/cockpit doctor --config cockpit.toml`,
|
|
267
|
+
still counts from the cockpit root.
|
|
268
|
+
`doctor` reports this as `COCKPIT_LAUNCHER_UNPROVEN`, using the guard's own check, and names which
|
|
269
|
+
spelling it can prove. The guard does not check which `npx` the command search path finds: it
|
|
270
|
+
assumes that program behaves as npm's own, as the version-manager and package-firewall shims that
|
|
271
|
+
hand over to npm do.
|
|
272
|
+
|
|
273
|
+
### Reading secrets and instances
|
|
274
|
+
|
|
275
|
+
Every hard denial still applies first. Reading a declared instance, a `.env` or `.env.*` file that
|
|
276
|
+
is not documentation, or anything under a `.secrets` directory is denied under every spelling that resolves to it, including a case-folded spelling of
|
|
277
|
+
the checkout itself, and so is a recursive search rooted at or below one. Shell reads also compare
|
|
278
|
+
filesystem identity: a hard link to a declared instance, or to any file in the cockpit's
|
|
279
|
+
`env/.secrets` directory, is denied like the file itself. The guard enumerates that directory on
|
|
280
|
+
every call, up to 512 entries; above that bound every content read falls back to a prompt rather
|
|
281
|
+
than going unchecked. A hard link to a file in any other `.secrets` directory is recognised only by
|
|
282
|
+
its spelling.
|
|
283
|
+
|
|
284
|
+
These reads fall back to a prompt instead of being allowed:
|
|
285
|
+
|
|
286
|
+
- a recursive search, whenever a declared instance or an `env/.secrets` file has a second hard link
|
|
287
|
+
anywhere;
|
|
288
|
+
- a search whose tree contains an existing instance, a `.secrets` directory at any depth, or a
|
|
289
|
+
`.env*` file that is neither a declared template nor one of the documentation names, since `grep -r` reads every file below it. The guard
|
|
290
|
+
walks each searched tree to find them, without following links, and a tree too large for its
|
|
291
|
+
bounded walk is not searched either. The reason names the path, so the agent can search a
|
|
292
|
+
narrower one;
|
|
293
|
+
- an `rg` search that would read one. ripgrep skips what Git ignores, so the guard asks Git, with
|
|
294
|
+
the guard's own Git executable, which files the enclosing repository does not ignore, and a
|
|
295
|
+
gitignored `.env` or `node_modules` does not block it. Only `.gitignore` files are applied, and
|
|
296
|
+
hidden files count, because a type such as `rg -t sh` reads a `.env`. Where Git's list cannot stand
|
|
297
|
+
for ripgrep's (outside a repository, with an `.ignore` or `.rgignore` file in or above the tree,
|
|
298
|
+
through a nested repository or submodule, below a `.git` or `.jj` entry, above which ripgrep stops
|
|
299
|
+
applying ignore files, when the search names an ignored path, when a `.gitignore` Git reads holds
|
|
300
|
+
a line the two glob engines may read differently, such as braces, a POSIX class like `[[:alpha:]]`,
|
|
301
|
+
a backslash escape, trailing whitespace, a `**` that is not a whole path component, or bytes that
|
|
302
|
+
are not UTF-8, or when Git does not answer), the
|
|
303
|
+
search is walked like `grep -r`. Either way the guard sees only what exists before the call runs, so a
|
|
304
|
+
recursive search is allowed only when every other command in the call is read-only too, and
|
|
305
|
+
nothing in it can write an environment file first;
|
|
306
|
+
- `git diff`, `git show`, and `git log` with patch output, whenever the repository's index holds a
|
|
307
|
+
`.env*` file that is neither a declared template nor a documentation name, a file below a
|
|
308
|
+
`.secrets` directory or a declared instance, unless the command names no revision and is limited with `-- <path>` to paths that hold
|
|
309
|
+
none (`git log` may name revisions, but not `--full-diff`). `--check` counts as printing content,
|
|
310
|
+
and `git log -L<range>:<path>` is not read-only. A revision may name a blob, which prints whatever pathspec
|
|
311
|
+
follows. Only the index is asked: a file committed once and since removed from it is still in
|
|
312
|
+
history, and `git log -p` prints it. Run `git rm --cached` and rotate what it held;
|
|
313
|
+
- a path that is a symlink or leaves one through `..`, lies outside the configured roots, or has
|
|
314
|
+
another `.env*` name, such as `.envrc`. A declared environment `template` and the documentation
|
|
315
|
+
names are read like any other file.
|
|
316
|
+
|
|
317
|
+
The guard hook sees shell commands and edits only. Claude Code's own Read, Grep and Glob tools never
|
|
318
|
+
reach it, so `sync` also writes Claude `Read` deny rules into `.claude/settings.json`: one for every
|
|
319
|
+
`.env` and every `.env.*` file, one for every `.secrets` directory and one for every declared instance
|
|
320
|
+
of every sibling, whether or not the
|
|
321
|
+
sibling is mounted or granted to Claude. A sibling instance's rule is anchored on the path below the
|
|
322
|
+
sibling's parent, so the committed file is identical on every clone; it can match more than the
|
|
323
|
+
instance (any file with the same sibling-relative suffix), never less. An instance in the cockpit
|
|
324
|
+
root itself is anchored at the project root. These rules match path spellings only: Claude's native
|
|
325
|
+
tools cannot see filesystem identity, so a hard link or a symbolic link to an instance or secrets
|
|
326
|
+
file, under a name outside the rules, stays readable to them. The guard's identity checks cover
|
|
327
|
+
shell commands only. Claude applies a deny rule before any allow, so the `.env.*` rule cannot exempt
|
|
328
|
+
the documentation names: Claude's own tools refuse `.env.example`, `.env.schema` and declared
|
|
329
|
+
templates too, which `cat` in a shell still reads.
|
|
330
|
+
|
|
331
|
+
## Who decides
|
|
332
|
+
|
|
333
|
+
`[autonomy]` and `[work.gates]` in `cockpit.toml` say who takes each decision cockpit gates: `agent`,
|
|
334
|
+
`person` or `ci`. Every field defaults to `agent` except `reset` and `mcp_approve`, which default to `person`.
|
|
335
|
+
`[work] grading` says how a work order earns done; the work-order fields are described in
|
|
336
|
+
[Who passes each gate](work-and-browser.md#who-passes-each-gate).
|
|
337
|
+
|
|
338
|
+
| Field | Decides | Default |
|
|
339
|
+
| --- | --- | --- |
|
|
340
|
+
| `sync` | whether the guard lets an agent run a writing `sync`; under `ci` a writing sync runs only where `CI` is set | `agent` |
|
|
341
|
+
| `stale_delete` | whether `sync` removes a file it generated before and no longer generates without a person typing the stale-removal digest | `agent` |
|
|
342
|
+
| `reset` | whether `reset --apply` deletes stack data without a person; otherwise a person approves the preview with `--interactive` | `person` |
|
|
343
|
+
| `mcp_approve` | `person`: sync enables only the MCP servers whose digest a person recorded in `[mcp.approved]`, and `MCP_NOT_APPROVED` names the rest; `agent`: sync enables every declared server as declared now, which lets an agent start processes outside the guard, because a sibling's MCP source is a file the agent can write and each harness starts an enabled server outside every guard boundary | `person` |
|
|
344
|
+
|
|
345
|
+
<!-- cockpit:ref path=packages/cockpit/src/guards/policy-pin.ts --> The guard pins the policy: an agent's edit, write or patch that would change `[work]`,
|
|
346
|
+
`[work.gates]` or `[autonomy]`, or `[mcp]` while `mcp_approve` is `person`, is a hard denial. It
|
|
347
|
+
applies the edit to the current text and compares the tables, so an edit elsewhere in the file stays
|
|
348
|
+
permitted; an edit it cannot apply exactly, and a shell command that names the file and is not a
|
|
349
|
+
proven read, a Git record or the `--config` operand of a cockpit command, are denied because the guard
|
|
350
|
+
cannot show the tables unchanged. A cockpit session's command after `--` is judged as a command of its
|
|
351
|
+
own, and a path or `node <script>` spelling of cockpit is admitted only where the guard proves it
|
|
352
|
+
starts the installed cockpit. The pin holds only for the
|
|
353
|
+
tool calls the guard inspects.
|
|
354
|
+
|
|
355
|
+
Every writing `sync` records the policy it ran under in `.cockpit/policy.json`. A later sync that finds
|
|
356
|
+
a field weaker than that record, such as a gate moved from `person` to `agent` or grading from
|
|
357
|
+
`independent` to `self`, writes nothing and reports `POLICY_WEAKENING_UNCONFIRMED` with the change and
|
|
358
|
+
its digest; a person runs `sync --interactive`, reads it and types the digest. `check` and `doctor` report
|
|
359
|
+
the same weakening before any sync runs. Until then every work
|
|
360
|
+
command refuses to run under the weaker policy, and so does `reset --apply` when `[autonomy] reset` is
|
|
361
|
+
the weakened field. A stronger policy needs nobody.
|
|
362
|
+
|
|
363
|
+
What no setting relaxes: reads of secrets and declared instances, pushes to protected branches,
|
|
364
|
+
writes to guard controls, and the policy pin. What only a person can do: trust Codex's hooks, trust a
|
|
365
|
+
workspace in Claude Code, and sign in to a secrets provider. `doctor` and `check` report missing
|
|
366
|
+
Codex hook trust as `CODEX_HOOK_UNTRUSTED`; cockpit cannot observe the other two, so a person
|
|
367
|
+
confirms them.
|
|
368
|
+
|
|
369
|
+
A local `person` gate is best-effort: cockpit cannot tell a person from a program driving a terminal.
|
|
370
|
+
`ci` relies on the `CI` variable, which an agent can set too, so locally it is best-effort as well.
|
|
371
|
+
The only enforced human gate is a required CI job behind a protected environment whose reviewers must
|
|
372
|
+
approve it, such as the [`work verify` job](work-and-browser.md#who-passes-each-gate).
|
|
373
|
+
|
|
374
|
+
## Native adapter behavior
|
|
375
|
+
|
|
376
|
+
The [transport and adapter owner](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/guards/harness-adapter.ts) translates a
|
|
377
|
+
decision without adding grants or weakening hard boundaries:
|
|
378
|
+
|
|
379
|
+
| Harness | Translation and adapter exit |
|
|
380
|
+
| --- | --- |
|
|
381
|
+
| Claude Code | Allow, ask, and deny exit 0; ask and deny are carried in native decision JSON. |
|
|
382
|
+
| Codex CLI | Ask becomes deny, because Codex hooks have no prompt tier, and a decision a human cannot be shown must not let the call run. Allow and deny exit 0; deny is carried in native decision JSON. |
|
|
383
|
+
| OpenCode | Ask becomes deny, for the same reason: its plugin hook can only throw or continue. Allow exits 0; every non-allow decision exits 2. |
|
|
384
|
+
|
|
385
|
+
Unavailable or malformed decisions exit 2 on every target, so adapter exit 0 alone does not mean
|
|
386
|
+
permission was granted.
|
|
387
|
+
|
|
388
|
+
Guard transport uses a fixed handler argument vector, normalized absolute handler and
|
|
389
|
+
working-directory paths, bounded standard input, and bounded output and error. The
|
|
390
|
+
[transport owner](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/guards/harness-adapter.ts) sets a timeout so a stalled
|
|
391
|
+
handler cannot hold a guarded call indefinitely. Copied plans, failed transport, signals, oversized
|
|
392
|
+
output, nonzero handler status and invalid JSON become `unavailable` with exit 2. Lexical path
|
|
393
|
+
normalization in this adapter does not itself verify filesystem identity.
|
|
394
|
+
|
|
395
|
+
If an adapter is unavailable, no target-specific registration or read-only fragment is emitted; the
|
|
396
|
+
shared guard file may still be generated. What each harness is confirmed to enforce, and why
|
|
397
|
+
generated restrictions stay `instructed` until a run of the real tool says otherwise, is in
|
|
398
|
+
[Capability states](harness-surfaces.md#capability-states) and
|
|
399
|
+
[Guard adapters](harness-surfaces.md#guard-adapters).
|
|
400
|
+
|
|
401
|
+
## Exit codes and generated references
|
|
402
|
+
|
|
403
|
+
Exit codes are listed in the [CLI reference](cli-reference.md). How cockpit records command
|
|
404
|
+
completion and keeps its generated reference pages current is maintainer detail, in the contributor
|
|
405
|
+
page "CLI completion and generated references", reached from [CONTRIBUTING.md](https://github.com/b0dea/cockpit/blob/main/CONTRIBUTING.md).
|
|
406
|
+
|
|
407
|
+
## Evidence
|
|
408
|
+
|
|
409
|
+
Static configuration, source inspection, synthetic fixtures, model self-report and silence do not
|
|
410
|
+
prove native enforcement. The
|
|
411
|
+
[native evidence validator](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/libexec/validate-harness-evidence.mjs) binds exact
|
|
412
|
+
current executable and wiring identities, generated-manifest and candidate digests, version,
|
|
413
|
+
bounded transcripts and validation results, and each record carries candidate and deliberately
|
|
414
|
+
broken-control evidence.
|
|
415
|
+
|
|
416
|
+
Missing evidence keeps a cell `instructed` or `unavailable`, with `human_needed` judgments still
|
|
417
|
+
pending. It never relaxes a hard denial or grants a permission. Switching defaults, deleting
|
|
418
|
+
fallbacks and publishing stay human decisions; a green source check or a generated reference does
|
|
419
|
+
not make them.
|