@b00dea/cockpit 0.0.0-stage → 0.1.1

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.
Files changed (426) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/LICENSE +21 -0
  3. package/README.md +25 -2
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2644 -0
  6. package/dist/autonomy.d.ts +45 -0
  7. package/dist/autonomy.js +173 -0
  8. package/dist/bounded-read.d.ts +14 -0
  9. package/dist/bounded-read.js +42 -0
  10. package/dist/browser/commands.d.ts +117 -0
  11. package/dist/browser/commands.js +453 -0
  12. package/dist/browser/package-paths.d.ts +1 -0
  13. package/dist/browser/package-paths.js +6 -0
  14. package/dist/browser/proxy.d.ts +22 -0
  15. package/dist/browser/proxy.js +37 -0
  16. package/dist/browser/runtime.d.ts +118 -0
  17. package/dist/browser/runtime.js +186 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +561 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/canonical-json.d.ts +2 -0
  23. package/dist/canonical-json.js +5 -0
  24. package/dist/cli-guard.d.ts +5 -0
  25. package/dist/cli-guard.js +71 -0
  26. package/dist/cli-main.d.ts +176 -0
  27. package/dist/cli-main.js +1584 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.js +16 -0
  30. package/dist/command-table.d.ts +121 -0
  31. package/dist/command-table.js +664 -0
  32. package/dist/config/load.d.ts +183 -0
  33. package/dist/config/load.js +367 -0
  34. package/dist/config/sibling-toml.d.ts +48 -0
  35. package/dist/config/sibling-toml.js +160 -0
  36. package/dist/config/surface-tools-toml.d.ts +9 -0
  37. package/dist/config/surface-tools-toml.js +37 -0
  38. package/dist/config/unconfirmed-toml.d.ts +9 -0
  39. package/dist/config/unconfirmed-toml.js +29 -0
  40. package/dist/config/verified-write.d.ts +43 -0
  41. package/dist/config/verified-write.js +89 -0
  42. package/dist/diagnostics.d.ts +26 -0
  43. package/dist/diagnostics.js +344 -0
  44. package/dist/docs/check.d.ts +16 -0
  45. package/dist/docs/check.js +197 -0
  46. package/dist/docs/decision-record.d.ts +34 -0
  47. package/dist/docs/decision-record.js +135 -0
  48. package/dist/docs/decisions.d.ts +16 -0
  49. package/dist/docs/decisions.js +103 -0
  50. package/dist/docs/markdown.d.ts +36 -0
  51. package/dist/docs/markdown.js +88 -0
  52. package/dist/docs/references.d.ts +32 -0
  53. package/dist/docs/references.js +148 -0
  54. package/dist/emit.d.ts +22 -0
  55. package/dist/emit.js +43 -0
  56. package/dist/entrypoint-kinds.d.ts +3 -0
  57. package/dist/entrypoint-kinds.js +11 -0
  58. package/dist/environment/application/confirm.d.ts +35 -0
  59. package/dist/environment/application/confirm.js +122 -0
  60. package/dist/environment/application/destructive.d.ts +6 -0
  61. package/dist/environment/application/destructive.js +429 -0
  62. package/dist/environment/application/doctor.d.ts +94 -0
  63. package/dist/environment/application/doctor.js +247 -0
  64. package/dist/environment/application/guard.d.ts +49 -0
  65. package/dist/environment/application/guard.js +677 -0
  66. package/dist/environment/application/health.d.ts +143 -0
  67. package/dist/environment/application/health.js +385 -0
  68. package/dist/environment/application/lifecycle-context.d.ts +235 -0
  69. package/dist/environment/application/lifecycle-context.js +154 -0
  70. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  71. package/dist/environment/application/lifecycle-state.js +202 -0
  72. package/dist/environment/application/lifecycle.d.ts +18 -0
  73. package/dist/environment/application/lifecycle.js +737 -0
  74. package/dist/environment/application/logs.d.ts +45 -0
  75. package/dist/environment/application/logs.js +127 -0
  76. package/dist/environment/application/services.d.ts +60 -0
  77. package/dist/environment/application/services.js +89 -0
  78. package/dist/environment/application/setup.d.ts +129 -0
  79. package/dist/environment/application/setup.js +741 -0
  80. package/dist/environment/application/shared.d.ts +11 -0
  81. package/dist/environment/application/shared.js +61 -0
  82. package/dist/environment/application/siblings.d.ts +43 -0
  83. package/dist/environment/application/siblings.js +269 -0
  84. package/dist/environment/claims.d.ts +389 -0
  85. package/dist/environment/claims.js +1701 -0
  86. package/dist/environment/codex-hook-trust.d.ts +11 -0
  87. package/dist/environment/codex-hook-trust.js +185 -0
  88. package/dist/environment/compose-environment.d.ts +1 -0
  89. package/dist/environment/compose-environment.js +6 -0
  90. package/dist/environment/compose-ownership.d.ts +21 -0
  91. package/dist/environment/compose-ownership.js +117 -0
  92. package/dist/environment/compose-topology.d.ts +125 -0
  93. package/dist/environment/compose-topology.js +490 -0
  94. package/dist/environment/contract.d.ts +522 -0
  95. package/dist/environment/contract.js +794 -0
  96. package/dist/environment/declaration.d.ts +14 -0
  97. package/dist/environment/declaration.js +39 -0
  98. package/dist/environment/destructive-composition.d.ts +29 -0
  99. package/dist/environment/destructive-composition.js +333 -0
  100. package/dist/environment/docker-daemon.d.ts +30 -0
  101. package/dist/environment/docker-daemon.js +78 -0
  102. package/dist/environment/docker-endpoint.d.ts +10 -0
  103. package/dist/environment/docker-endpoint.js +19 -0
  104. package/dist/environment/env-schema-observation.d.ts +46 -0
  105. package/dist/environment/env-schema-observation.js +241 -0
  106. package/dist/environment/evidence.d.ts +136 -0
  107. package/dist/environment/evidence.js +437 -0
  108. package/dist/environment/executable.d.ts +38 -0
  109. package/dist/environment/executable.js +190 -0
  110. package/dist/environment/generated-application/authorities.d.ts +5 -0
  111. package/dist/environment/generated-application/authorities.js +61 -0
  112. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  113. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  114. package/dist/environment/generated-application/check.d.ts +13 -0
  115. package/dist/environment/generated-application/check.js +191 -0
  116. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  117. package/dist/environment/generated-application/custom-surface.js +289 -0
  118. package/dist/environment/generated-application/prepare.d.ts +29 -0
  119. package/dist/environment/generated-application/prepare.js +278 -0
  120. package/dist/environment/generated-application/publication.d.ts +33 -0
  121. package/dist/environment/generated-application/publication.js +547 -0
  122. package/dist/environment/generated-application/shared.d.ts +492 -0
  123. package/dist/environment/generated-application/shared.js +354 -0
  124. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  125. package/dist/environment/generated-application/surface-observation.js +69 -0
  126. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  127. package/dist/environment/generated-application/surface-outputs.js +400 -0
  128. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  129. package/dist/environment/generated-application/surfaces.js +704 -0
  130. package/dist/environment/generated-application/sync.d.ts +6 -0
  131. package/dist/environment/generated-application/sync.js +124 -0
  132. package/dist/environment/git-observation.d.ts +202 -0
  133. package/dist/environment/git-observation.js +553 -0
  134. package/dist/environment/health-composition.d.ts +12 -0
  135. package/dist/environment/health-composition.js +645 -0
  136. package/dist/environment/host-observation.d.ts +56 -0
  137. package/dist/environment/host-observation.js +52 -0
  138. package/dist/environment/identity.d.ts +48 -0
  139. package/dist/environment/identity.js +85 -0
  140. package/dist/environment/init-application.d.ts +333 -0
  141. package/dist/environment/init-application.js +726 -0
  142. package/dist/environment/install-policy-observation.d.ts +71 -0
  143. package/dist/environment/install-policy-observation.js +284 -0
  144. package/dist/environment/install-policy.d.ts +48 -0
  145. package/dist/environment/install-policy.js +96 -0
  146. package/dist/environment/instance.d.ts +33 -0
  147. package/dist/environment/instance.js +122 -0
  148. package/dist/environment/instruction-pointer.d.ts +19 -0
  149. package/dist/environment/instruction-pointer.js +144 -0
  150. package/dist/environment/lifecycle-composition.d.ts +31 -0
  151. package/dist/environment/lifecycle-composition.js +742 -0
  152. package/dist/environment/lifecycle-stage.d.ts +120 -0
  153. package/dist/environment/lifecycle-stage.js +408 -0
  154. package/dist/environment/lifecycle.d.ts +75 -0
  155. package/dist/environment/lifecycle.js +354 -0
  156. package/dist/environment/logs.d.ts +132 -0
  157. package/dist/environment/logs.js +519 -0
  158. package/dist/environment/map-limit.d.ts +2 -0
  159. package/dist/environment/map-limit.js +13 -0
  160. package/dist/environment/mcp-observation.d.ts +9 -0
  161. package/dist/environment/mcp-observation.js +46 -0
  162. package/dist/environment/observations.d.ts +46 -0
  163. package/dist/environment/observations.js +346 -0
  164. package/dist/environment/operator-application.d.ts +79 -0
  165. package/dist/environment/operator-application.js +197 -0
  166. package/dist/environment/ports.d.ts +182 -0
  167. package/dist/environment/ports.js +1205 -0
  168. package/dist/environment/posture-observation.d.ts +68 -0
  169. package/dist/environment/posture-observation.js +253 -0
  170. package/dist/environment/presence.d.ts +4 -0
  171. package/dist/environment/presence.js +26 -0
  172. package/dist/environment/read-composition.d.ts +32 -0
  173. package/dist/environment/read-composition.js +397 -0
  174. package/dist/environment/readiness-composition.d.ts +57 -0
  175. package/dist/environment/readiness-composition.js +151 -0
  176. package/dist/environment/reclaim.d.ts +370 -0
  177. package/dist/environment/reclaim.js +1940 -0
  178. package/dist/environment/repository-identity.d.ts +13 -0
  179. package/dist/environment/repository-identity.js +108 -0
  180. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  181. package/dist/environment/runtime-measurement-record.js +111 -0
  182. package/dist/environment/runtime-measurement.d.ts +27 -0
  183. package/dist/environment/runtime-measurement.js +224 -0
  184. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  185. package/dist/environment/runtime-probe-residue.js +148 -0
  186. package/dist/environment/runtime-probe.d.ts +72 -0
  187. package/dist/environment/runtime-probe.js +725 -0
  188. package/dist/environment/runtime.d.ts +551 -0
  189. package/dist/environment/runtime.js +2405 -0
  190. package/dist/environment/seed.d.ts +16 -0
  191. package/dist/environment/seed.js +43 -0
  192. package/dist/environment/selftest-application.d.ts +86 -0
  193. package/dist/environment/selftest-application.js +713 -0
  194. package/dist/environment/sensitive.d.ts +3 -0
  195. package/dist/environment/sensitive.js +48 -0
  196. package/dist/environment/served-checkout-grants.d.ts +53 -0
  197. package/dist/environment/served-checkout-grants.js +182 -0
  198. package/dist/environment/setup-observation.d.ts +62 -0
  199. package/dist/environment/setup-observation.js +1473 -0
  200. package/dist/environment/setup.d.ts +43 -0
  201. package/dist/environment/setup.js +176 -0
  202. package/dist/environment/sibling-checkouts.d.ts +119 -0
  203. package/dist/environment/sibling-checkouts.js +254 -0
  204. package/dist/environment/sibling-instructions.d.ts +33 -0
  205. package/dist/environment/sibling-instructions.js +224 -0
  206. package/dist/environment/sibling-observation.d.ts +27 -0
  207. package/dist/environment/sibling-observation.js +45 -0
  208. package/dist/environment/sibling-pull.d.ts +23 -0
  209. package/dist/environment/sibling-pull.js +95 -0
  210. package/dist/environment/sibling-registration.d.ts +45 -0
  211. package/dist/environment/sibling-registration.js +538 -0
  212. package/dist/environment/stack-inputs.d.ts +18 -0
  213. package/dist/environment/stack-inputs.js +210 -0
  214. package/dist/environment/stack-inventory-prune.d.ts +31 -0
  215. package/dist/environment/stack-inventory-prune.js +129 -0
  216. package/dist/environment/stack-inventory-report.d.ts +44 -0
  217. package/dist/environment/stack-inventory-report.js +78 -0
  218. package/dist/environment/stack-inventory.d.ts +94 -0
  219. package/dist/environment/stack-inventory.js +418 -0
  220. package/dist/environment/state.d.ts +410 -0
  221. package/dist/environment/state.js +2153 -0
  222. package/dist/environment/templates.d.ts +140 -0
  223. package/dist/environment/templates.js +685 -0
  224. package/dist/environment/tool-registration.d.ts +9 -0
  225. package/dist/environment/tool-registration.js +92 -0
  226. package/dist/environment/upgrade-application.d.ts +19 -0
  227. package/dist/environment/upgrade-application.js +175 -0
  228. package/dist/environment-exec/execute.d.ts +105 -0
  229. package/dist/environment-exec/execute.js +1179 -0
  230. package/dist/environment-exec/libexec-program.d.ts +6 -0
  231. package/dist/environment-exec/libexec-program.js +22 -0
  232. package/dist/environment-exec/verified-digest.d.ts +4 -0
  233. package/dist/environment-exec/verified-digest.js +16 -0
  234. package/dist/exact-keys.d.ts +7 -0
  235. package/dist/exact-keys.js +15 -0
  236. package/dist/fix-catalogue.d.ts +32 -0
  237. package/dist/fix-catalogue.js +60 -0
  238. package/dist/freshness.d.ts +29 -0
  239. package/dist/freshness.js +75 -0
  240. package/dist/generated-environment/adopt.d.ts +16 -0
  241. package/dist/generated-environment/adopt.js +271 -0
  242. package/dist/generated-environment/command.d.ts +47 -0
  243. package/dist/generated-environment/command.js +75 -0
  244. package/dist/generated-environment/transaction.d.ts +186 -0
  245. package/dist/generated-environment/transaction.js +1770 -0
  246. package/dist/grading/evaluate.d.ts +71 -0
  247. package/dist/grading/evaluate.js +403 -0
  248. package/dist/grading/grade.d.ts +33 -0
  249. package/dist/grading/grade.js +57 -0
  250. package/dist/guards/answer-confirmation.d.ts +41 -0
  251. package/dist/guards/answer-confirmation.js +109 -0
  252. package/dist/guards/evaluator.d.ts +130 -0
  253. package/dist/guards/evaluator.js +1258 -0
  254. package/dist/guards/harness-adapter.d.ts +17 -0
  255. package/dist/guards/harness-adapter.js +57 -0
  256. package/dist/guards/host.d.ts +8 -0
  257. package/dist/guards/host.js +10 -0
  258. package/dist/guards/invocation-parser.d.ts +66 -0
  259. package/dist/guards/invocation-parser.js +595 -0
  260. package/dist/guards/local-identity.d.ts +44 -0
  261. package/dist/guards/local-identity.js +129 -0
  262. package/dist/guards/policy-change.d.ts +43 -0
  263. package/dist/guards/policy-change.js +159 -0
  264. package/dist/guards/policy-pin.d.ts +13 -0
  265. package/dist/guards/policy-pin.js +177 -0
  266. package/dist/guards/protected-locations.d.ts +62 -0
  267. package/dist/guards/protected-locations.js +461 -0
  268. package/dist/guards/read-only.d.ts +37 -0
  269. package/dist/guards/read-only.js +315 -0
  270. package/dist/init/command.d.ts +41 -0
  271. package/dist/init/command.js +330 -0
  272. package/dist/init/derive.d.ts +53 -0
  273. package/dist/init/derive.js +819 -0
  274. package/dist/init/draft.d.ts +140 -0
  275. package/dist/init/draft.js +1228 -0
  276. package/dist/init/render.d.ts +47 -0
  277. package/dist/init/render.js +104 -0
  278. package/dist/init/scaffold.d.ts +53 -0
  279. package/dist/init/scaffold.js +121 -0
  280. package/dist/invocation.d.ts +13 -0
  281. package/dist/invocation.js +20 -0
  282. package/dist/package-metadata.d.ts +9 -0
  283. package/dist/package-metadata.js +19 -0
  284. package/dist/readiness.d.ts +53 -0
  285. package/dist/readiness.js +88 -0
  286. package/dist/references/named.d.ts +24 -0
  287. package/dist/references/named.js +27 -0
  288. package/dist/refusal.d.ts +15 -0
  289. package/dist/refusal.js +7 -0
  290. package/dist/registry-derivation.d.ts +31 -0
  291. package/dist/registry-derivation.js +32 -0
  292. package/dist/registry-resolve/command.d.ts +47 -0
  293. package/dist/registry-resolve/command.js +226 -0
  294. package/dist/registry-resolve/registry.d.ts +68 -0
  295. package/dist/registry-resolve/registry.js +292 -0
  296. package/dist/registry-resolve/render-shell.d.ts +18 -0
  297. package/dist/registry-resolve/render-shell.js +36 -0
  298. package/dist/repair.d.ts +29 -0
  299. package/dist/repair.js +8 -0
  300. package/dist/repository-path.d.ts +9 -0
  301. package/dist/repository-path.js +16 -0
  302. package/dist/schema-json.d.ts +1 -0
  303. package/dist/schema-json.js +177 -0
  304. package/dist/schema.d.ts +1289 -0
  305. package/dist/schema.js +985 -0
  306. package/dist/setup/command.d.ts +4 -0
  307. package/dist/setup/command.js +4 -0
  308. package/dist/sha256.d.ts +1 -0
  309. package/dist/sha256.js +4 -0
  310. package/dist/surfaces/adoption.d.ts +41 -0
  311. package/dist/surfaces/adoption.js +217 -0
  312. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  313. package/dist/surfaces/consumer-instructions.js +43 -0
  314. package/dist/surfaces/contract.d.ts +42 -0
  315. package/dist/surfaces/contract.js +169 -0
  316. package/dist/surfaces/core-declarations.d.ts +24 -0
  317. package/dist/surfaces/core-declarations.js +79 -0
  318. package/dist/surfaces/development-markers.d.ts +1 -0
  319. package/dist/surfaces/development-markers.js +18 -0
  320. package/dist/surfaces/generator-adapter.d.ts +71 -0
  321. package/dist/surfaces/generator-adapter.js +809 -0
  322. package/dist/surfaces/harness-registry.d.ts +260 -0
  323. package/dist/surfaces/harness-registry.js +146 -0
  324. package/dist/surfaces/mcp.d.ts +145 -0
  325. package/dist/surfaces/mcp.js +349 -0
  326. package/dist/surfaces/merge.d.ts +30 -0
  327. package/dist/surfaces/merge.js +105 -0
  328. package/dist/surfaces/ownership.d.ts +190 -0
  329. package/dist/surfaces/ownership.js +795 -0
  330. package/dist/surfaces/projection.d.ts +136 -0
  331. package/dist/surfaces/projection.js +716 -0
  332. package/dist/surfaces/render.d.ts +79 -0
  333. package/dist/surfaces/render.js +636 -0
  334. package/dist/surfaces/restrictions.d.ts +130 -0
  335. package/dist/surfaces/restrictions.js +447 -0
  336. package/dist/surfaces/selftest.d.ts +162 -0
  337. package/dist/surfaces/selftest.js +393 -0
  338. package/dist/surfaces/skill-contract.d.ts +85 -0
  339. package/dist/surfaces/skill-contract.js +449 -0
  340. package/dist/surfaces/sync.d.ts +57 -0
  341. package/dist/surfaces/sync.js +164 -0
  342. package/dist/team-metadata.d.ts +41 -0
  343. package/dist/team-metadata.js +513 -0
  344. package/dist/testing/gates.d.ts +91 -0
  345. package/dist/testing/gates.js +182 -0
  346. package/dist/testing/hooks.d.ts +59 -0
  347. package/dist/testing/hooks.js +139 -0
  348. package/dist/testing/test-protection.d.ts +34 -0
  349. package/dist/testing/test-protection.js +137 -0
  350. package/dist/testing/verdict.d.ts +29 -0
  351. package/dist/testing/verdict.js +62 -0
  352. package/dist/testing/weakening.d.ts +15 -0
  353. package/dist/testing/weakening.js +30 -0
  354. package/dist/work-orders/application-closure.d.ts +7 -0
  355. package/dist/work-orders/application-closure.js +76 -0
  356. package/dist/work-orders/application-context.d.ts +162 -0
  357. package/dist/work-orders/application-context.js +137 -0
  358. package/dist/work-orders/application-decisions.d.ts +27 -0
  359. package/dist/work-orders/application-decisions.js +121 -0
  360. package/dist/work-orders/application-grading.d.ts +11 -0
  361. package/dist/work-orders/application-grading.js +212 -0
  362. package/dist/work-orders/application.d.ts +6 -0
  363. package/dist/work-orders/application.js +223 -0
  364. package/dist/work-orders/composition.d.ts +101 -0
  365. package/dist/work-orders/composition.js +1202 -0
  366. package/dist/work-orders/contract.d.ts +77 -0
  367. package/dist/work-orders/contract.js +140 -0
  368. package/dist/work-orders/decisions.d.ts +23 -0
  369. package/dist/work-orders/decisions.js +55 -0
  370. package/dist/work-orders/store.d.ts +207 -0
  371. package/dist/work-orders/store.js +291 -0
  372. package/docs/README.md +68 -0
  373. package/docs/architecture.md +136 -0
  374. package/docs/capability-matrix.md +58 -0
  375. package/docs/cli-reference.md +807 -0
  376. package/docs/command-list.md +62 -0
  377. package/docs/compose-patterns.md +99 -0
  378. package/docs/concepts.md +173 -0
  379. package/docs/configuration-essentials.md +177 -0
  380. package/docs/configuration-reference.md +636 -0
  381. package/docs/docs-check.md +76 -0
  382. package/docs/environment.md +948 -0
  383. package/docs/extending.md +43 -0
  384. package/docs/faq.md +113 -0
  385. package/docs/getting-started.md +692 -0
  386. package/docs/harness-surfaces.md +500 -0
  387. package/docs/joining-a-team.md +76 -0
  388. package/docs/release-policy.md +49 -0
  389. package/docs/safety-and-autonomy.md +463 -0
  390. package/docs/troubleshooting.md +382 -0
  391. package/docs/tutorial.md +171 -0
  392. package/docs/work-and-browser.md +420 -0
  393. package/libexec/browser-operation.cjs +270 -0
  394. package/libexec/fence-marker.cjs +101 -0
  395. package/libexec/fence-marker.d.cts +24 -0
  396. package/libexec/fenced-group-runner.cjs +77 -0
  397. package/libexec/fenced-supervisor.cjs +137 -0
  398. package/libexec/generated-filesystem.cjs +731 -0
  399. package/libexec/install-filesystem.cjs +225 -0
  400. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  401. package/libexec/rulesync-generator.cjs +743 -0
  402. package/libexec/surface-generator-worker.cjs +266 -0
  403. package/libexec/surface-output-reader.cjs +99 -0
  404. package/libexec/surface-scratch-cleanup.cjs +14 -0
  405. package/libexec/validate-harness-evidence.d.mts +183 -0
  406. package/libexec/validate-harness-evidence.mjs +1312 -0
  407. package/package.json +74 -4
  408. package/surfaces/README.md +33 -0
  409. package/surfaces/agents/evaluator.agent.md +76 -0
  410. package/surfaces/guards/bash-guard.sh +48 -0
  411. package/surfaces/guards/claude.adapter.json +25 -0
  412. package/surfaces/guards/codex.adapter.json +24 -0
  413. package/surfaces/guards/opencode.adapter.json +25 -0
  414. package/surfaces/references/browser.md +63 -0
  415. package/surfaces/references/test-data-and-personas.md +84 -0
  416. package/surfaces/references/testing-tiers.md +11 -0
  417. package/surfaces/skills/cockpit-browser.skill.md +59 -0
  418. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  419. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  420. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  421. package/surfaces/skills/cockpit-work.skill.md +34 -0
  422. package/surfaces/skills/collection.json +5 -0
  423. package/surfaces/skills/procedures/explain.md +66 -0
  424. package/surfaces/skills/procedures/fix.md +95 -0
  425. package/surfaces/skills/procedures/setup.md +452 -0
  426. package/surfaces/skills/procedures/work.md +134 -0
@@ -0,0 +1,948 @@
1
+ # Environment lifecycle
2
+
3
+ This page is the reference for cockpit's environment commands: what each one does, what it
4
+ refuses, and how to recover when it refuses. It is for people running cockpit on a product and for
5
+ agents working inside it. For the order to run the commands in, follow
6
+ [Bring the product up](getting-started.md#bring-the-product-up); every flag is in the
7
+ [CLI reference](cli-reference.md), and `cockpit <command> --help` prints a command's usage.
8
+
9
+ ## Output and exit codes
10
+
11
+ Add `--json` to get the result and any diagnostics as one JSON document on stdout. Without
12
+ `--json`, cockpit prints the result as readable text and diagnostics to stderr. Every refusal exits
13
+ nonzero with a named diagnostic code, and unknown flags are refused. `logs` defaults to
14
+ `--tail 100 --since 15m`.
15
+
16
+ ## Input limits
17
+
18
+ cockpit checks the size of configuration, registry and adoption files before parsing them, against
19
+ the limit defined in [`config/load.ts`](https://github.com/b0dea/cockpit/blob/main/packages/cockpit/src/config/load.ts). A file that cannot
20
+ be read, is not a regular file, is too large, or changes while cockpit reads it is refused with a
21
+ diagnostic. Make the file smaller; raising an output or subprocess limit does not change this one.
22
+
23
+ A skipped registry row produces no `init --registry` draft, because cockpit may no longer know
24
+ which repository it applies to. `registry-resolve` prints shell data you can `source` only when its
25
+ input passed validation.
26
+
27
+ ## Environment modes
28
+
29
+ `[environment]` is optional. What it declares decides which commands have anything to run.
30
+
31
+ | Declares | Mode | `sync` and `check` | Runtime commands |
32
+ |---|---|---|---|
33
+ | no `compose_files` | surfaces only | write and drift-check the harness surfaces | refuse with `ENVIRONMENT_NOT_DECLARED` |
34
+ | `environment.compose_files` | compose first | the surfaces, plus `check` reads the Compose topology | run the declared Compose files |
35
+
36
+ The runtime commands are `up`, `sleep`, `session`, `down`,
37
+ `reset`, `preflight`, `logs`, `ps`, `restart`, `rebuild`, `exec` and `runtime measure`. Every mode writes the harness
38
+ [surfaces](concepts.md#surface) (instructions, skills, guards, permissions, evaluator and work
39
+ orders) and checks them for [drift](concepts.md#drift). A cockpit that declares only
40
+ `[environment.machine]` still gets the machine-capacity checks in `preflight`, so it is not refused
41
+ as surfaces only. `doctor` reports an undeclared runtime as not declared, not as healthy, and its
42
+ `declarations` result says whether the runtime, the reset volumes and the seed command are
43
+ declared. [`examples/docs-only`](https://github.com/b0dea/cockpit/blob/main/examples/docs-only/README.md) and
44
+ [`examples/compose-first`](https://github.com/b0dea/cockpit/blob/main/examples/compose-first/README.md) show the two modes. Vendor stubs,
45
+ TLS, seeding and restoring data are plain Compose; [Compose patterns](compose-patterns.md) shows
46
+ each.
47
+
48
+ ### Compose-first stacks
49
+
50
+ `environment.compose_files` lists paths relative to the cockpit root, and each must stay inside it.
51
+ Compose resolves every relative path in those files, such as `build: ./api` or `"./api:/app"`,
52
+ against the directory of the first listed file, exactly as `docker compose -f <first> -f <second>`
53
+ does. So `compose_files = ["shop/compose.yml"]` runs a product repository's own file with its `./`
54
+ paths meaning what they mean in that repository. Every command passes that directory to Compose as
55
+ `--project-directory`, because Compose reads verified snapshots of the files and could not
56
+ otherwise tell where they came from. List files from two directories only when the later ones name
57
+ no relative paths, since theirs resolve against the first file's directory too.
58
+
59
+ `check` reads your Compose topology with `docker compose config`, without interpolation, and refuses
60
+ with `COMPOSE_TOPOLOGY_UNAVAILABLE` if Docker cannot produce it. It also checks:
61
+
62
+ - a published host port not written as a `[ports.bases]` variable, such as `"127.0.0.1:4200:3000"`,
63
+ fails with `COMPOSE_PORT_NOT_LEASED`, because the same number collides across worktrees and
64
+ parallel stacks; write `"127.0.0.1:${API_HOST_PORT}:3000"` instead;
65
+ - a published port with no host interface, such as `"${API_HOST_PORT}:3000"`, warns with
66
+ `COMPOSE_PORT_ALL_INTERFACES`, because Docker then binds every interface and the service is
67
+ reachable from the network; write `"127.0.0.1:${API_HOST_PORT}:3000"`, or name `0.0.0.0` when you
68
+ mean the wider exposure;
69
+ - a service that sets `container_name` fails with `COMPOSE_CONTAINER_NAME_FIXED`, because Docker allows
70
+ one container of a name and the second checkout's stack would fail to start; remove it and address
71
+ the service by its Compose service name, or with `docker compose exec <service>`;
72
+ - a service with `build` and a fixed `image` fails with `COMPOSE_IMAGE_TAG_SHARED`, because every
73
+ checkout builds to the same tag and one checkout's build replaces the image another's stack runs;
74
+ remove `image`, so Compose names the build after the stack's own project;
75
+ - a profile in `environment.profiles` or a `profile:<name>` mount that no service declares fails
76
+ with `COMPOSE_PROFILE_UNDEFINED`, because Compose accepts an unknown profile and starts nothing
77
+ for it;
78
+ - a service with no Compose healthcheck warns with `COMPOSE_HEALTHCHECK_MISSING`, because running
79
+ is then treated as ready;
80
+ - an image referenced by tag rather than digest warns with `COMPOSE_IMAGE_UNPINNED`.
81
+
82
+ `doctor` reports a refusal from this check as itself, with its fix, not as the surface evidence it makes
83
+ unavailable. `init` shows the port, container name, shared image tag and healthcheck findings beside the drafted `cockpit.toml`, as
84
+ warnings carrying the same fix; it never edits a Compose file, so apply the edit before or right after
85
+ accepting.
86
+
87
+ cockpit never edits your Compose files. To run the stack, `sync` writes `compose.cockpit.yml`, an
88
+ override that belongs to cockpit and adds its ownership labels to every service, the default
89
+ network and every named volume that is not `external`. Volumes also get a generation label, so
90
+ `reset` can account for each `resettable_volumes` entry. `check` drift-checks the override, and
91
+ runtime commands pass it after your declared files only while it matches what cockpit would
92
+ generate now. A Compose `include` is refused with `GENERATED_DECLARATION_INVALID`, because Compose
93
+ reads an included file from disk, outside the verified snapshot, and cockpit could not label the
94
+ services it brings in; list the file in `environment.compose_files` instead.
95
+
96
+ Runtime commands give Compose only these variables: the leased `[ports.bases]` variables, each
97
+ `[[sibling]] var` set to that sibling's absolute directory, and cockpit's own stack and holder
98
+ identities. Any other `${VAR}` reference needs a `:-default`. Every Compose command that
99
+ interpolates the checked configuration turns off Compose's automatic loading of the project `.env`
100
+ file, because a file cockpit has not checked must not change the topology it has checked. The
101
+ topology check does not read service `env_file` contents either, so service secrets never enter
102
+ cockpit's records; Compose still passes those files to the containers when the stack runs.
103
+
104
+ Mounts must match the declaration in both directions:
105
+
106
+ - Each sibling declared with `mount = "always"` must be bind-mounted, whole or a directory inside
107
+ it. Otherwise `check` fails with `COMPOSE_SIBLING_NOT_MOUNTED` and `up` refuses to start the
108
+ stack. Mount it through its variable, as in `"${WORKER_DIR}:/srv/worker"` or
109
+ `"${WORKER_DIR}/src:/app"`, through a path relative to the first Compose file's directory, as in
110
+ `"./worker:/srv/worker"` from a `compose.yml` in the cockpit root, or set `mount = "none"` on the
111
+ row.
112
+ - Each `environment.generated_host_paths` entry must reach a container: bound itself, through a
113
+ directory inside it, or inside a bound directory such as a sibling mounted whole. Otherwise
114
+ `check` fails with `GENERATED_HOST_PATH_UNMOUNTED`.
115
+ - A container gets only what `cockpit.toml` declares. A bind outside every `mount = "always"`
116
+ sibling and every `environment.generated_host_paths` entry, or one that leaves its sibling
117
+ through a symbolic link, fails `check` with `COMPOSE_BIND_UNDECLARED`, and `up` refuses it.
118
+
119
+ ### Scaffold a Compose file
120
+
121
+ A small app with no Compose file can get a starting one, once:
122
+
123
+ ```sh
124
+ npx --no cockpit init . --scaffold-compose api --port 3000 --with postgres,redis
125
+ ```
126
+
127
+ `--scaffold-compose` names the app directory relative to the cockpit root (`.` for the root
128
+ itself) and `--port` the port the app listens on inside its container. The app builds from its
129
+ Dockerfile; without one, a directory with a `package.json` runs on `node:22-alpine` and needs
130
+ `--command "npm start"` or whatever starts it. `--with` adds a database or cache from a fixed list,
131
+ `postgres` and `redis`, each on a pinned major version with a healthcheck, a named volume and its
132
+ URL in the app's environment. The written `compose.yml` publishes the app on
133
+ `127.0.0.1:${API_HOST_PORT:-3000}` and bind-mounts the app directory, and the command prints the
134
+ `[ports.bases]` variable and `resettable_volumes` entries to declare. `check` then warns that the
135
+ app service has no healthcheck and that the images are pinned by tag; add a healthcheck and pin
136
+ each image by digest when you can. The file is yours from then
137
+ on: cockpit runs it as written and never rewrites it, and the scaffold refuses with
138
+ `INIT_SCAFFOLD_COMPOSE_EXISTS` when the cockpit root already holds any Compose file.
139
+
140
+ ### Compose profiles
141
+
142
+ A service with `profiles: [workers]` in the Compose file runs only while that profile is active.
143
+ `environment.profiles` lists the profiles every `up` and `session` activates, and
144
+ `up --profile <name>` or `session --profile <name>`, repeated for several, adds more for that run.
145
+ A sibling declared `mount = "profile:<name>"` is mounted, and checked as mounted, exactly while its
146
+ profile is active; the generated agent files follow `environment.profiles`. A profile no service
147
+ declares is refused with `COMPOSE_PROFILE_UNDEFINED`, by `check` for the declared ones and by `up`
148
+ for the requested ones.
149
+
150
+ The stack records the profiles it started with, and every later command on it, `down`, `sleep`,
151
+ `logs`, `preflight`, `ps` and the service verbs included, activates the same set, so `down` removes
152
+ the profile services too. A plain `up` or `session` joins the running stack with its recorded set.
153
+ Adding a profile changes what the stack runs, so `up` refuses it on a running stack with
154
+ `LIFECYCLE_PROGRESS_INVALID`, as it refuses any change to what the stack was started from: run
155
+ `down`, then `up --profile <name>`. `check` judges each service's bind mounts under the default
156
+ set and under the default set plus each other profile, so a bind that only one profile set would
157
+ refuse still fails `check`.
158
+
159
+ ## Local URLs and port families
160
+
161
+ Write each published port with an explicit loopback binding in your Compose file, as in
162
+ `"127.0.0.1:${API_HOST_PORT}:3000"`, so starting a local stack does not expose it to the network;
163
+ use `::1` for IPv6 loopback, or `0.0.0.0` only when you intend that wider exposure. cockpit does not
164
+ rewrite your published interfaces.
165
+
166
+ Stack *n* publishes each `[ports.bases]` value plus *n* × `ports.step`, for up to `ports.count`
167
+ stacks (defaults: step 100, count 27). `up` leases a whole port family under one lock shared by
168
+ every cockpit run by your user account with the same canonical
169
+ [state root](troubleshooting.md#clear-cockpit-state). It skips any family holding a port that
170
+ another cockpit's active lease holds, then checks that no other program is listening on
171
+ the ports it picked. Cockpits with similar bases therefore never get the same ports, even before
172
+ either stack starts listening; an overlap costs a candidate family, never a shared port. Each
173
+ cockpit's stack count applies only to its own stacks. A retained lease keeps its original family
174
+ and blocks any conflicting reservation. Other user accounts and separate state roots, including different Linux `XDG_STATE_HOME`
175
+ settings, are not coordinated; only the listener check separates them.
176
+
177
+ The listener check looks for a listener on each port on IPv4 loopback when ports are first
178
+ allocated. It does not reserve the port, does not see a listener bound only to IPv6, and cannot
179
+ stop another process binding the port afterwards. If startup reports a bind failure, find the
180
+ conflicting process, resolve it, and rerun `cockpit up --config cockpit.toml`. The lease stays
181
+ reserved until cockpit has confirmed its cleanup, even when its holder is no longer running. To
182
+ change a stack's ports, see [Change a running stack](#change-a-running-stack).
183
+
184
+ ### Named local URLs
185
+
186
+ Entrypoint derivatives turn a port lease into application origins, callback URLs and other
187
+ declared addresses. Add an optional `hostname` and `scheme` to use a named local URL:
188
+
189
+ ```toml
190
+ [ports.bases]
191
+ APP_HOST_PORT = 8443
192
+
193
+ [[environment.entrypoint_derivatives]]
194
+ name = "application"
195
+ kind = "origin"
196
+ port = "APP_HOST_PORT"
197
+ path = "/"
198
+ hostname = "app.sample.localhost"
199
+ scheme = "https"
200
+ ```
201
+
202
+ Run `cockpit up --config cockpit.toml --json` and use the entrypoint it returns; the example
203
+ produces `https://app.sample.localhost:<leased-port>/`. The port comes from the selected port
204
+ family, including any nonzero offset, and a retained lease derives it the same way. Omitting
205
+ `hostname` uses `127.0.0.1`; omitting `scheme` uses `http`. Automatic numeric entrypoints keep their
206
+ plain HTTP loopback URLs. These declarations set no environment variables in the application:
207
+ configure the product's public origin and callback settings to match the returned address.
208
+
209
+ Names must be lowercase DNS subdomains of `.localhost`, with ordinary letter, digit and hyphen
210
+ labels. Arbitrary suffixes, bare `localhost`, wildcard names and embedded ports are refused.
211
+ [RFC 6761](https://www.rfc-editor.org/rfc/rfc6761.html#section-6.3) reserves `.localhost` for
212
+ loopback; an arbitrary name such as `local.app.sample` has no such guarantee. Use a distinct
213
+ hostname for each project, because
214
+ [cookies are not isolated by port](https://www.rfc-editor.org/rfc/rfc6265.html#section-8.5).
215
+
216
+ Host clients use the host resolver, and conforming resolvers map `.localhost` names to host
217
+ loopback. Inside a container, loopback is that container's own, so a product container that calls
218
+ the host-published URL needs its own `extra_hosts` mapping to the right host gateway;
219
+ service-to-service traffic normally uses Compose service names and container ports instead.
220
+ `browser.resolver` supplies mappings only to the browser the `browser` commands launch, not to an
221
+ unrelated remote browser, MCP server or other client: put the mapping where that client's network
222
+ process runs. Browser operations then use that browser's own networking and DNS, as described in
223
+ [Work and browser](work-and-browser.md#browser-evidence).
224
+
225
+ `scheme = "https"` only declares a URL; the product's Compose stack must serve TLS on that leased
226
+ port. cockpit does not install trust roots, change host DNS or start a shared HTTPS proxy. To set
227
+ it up:
228
+
229
+ 1. Provision a certificate whose subjectAltName covers the exact hostname. Port offsets do not
230
+ change the certificate identity.
231
+ 2. Keep private keys outside tracked files and build contexts, and mount only the leaf key into
232
+ the TLS service.
233
+ 3. Explicitly trust the issuer in each runtime that connects. Clients need the public CA
234
+ certificate, never the CA signing key.
235
+ 4. Rerun `cockpit up --config cockpit.toml` and open the returned URL with certificate validation
236
+ enabled.
237
+
238
+ [Compose patterns](compose-patterns.md#serve-tls) shows a TLS service in front of the app.
239
+
240
+ ## Sibling and template paths
241
+
242
+ `sync` and `check` require each declared [sibling](concepts.md#sibling-repository) directory to
243
+ exist inside the siblings root, both as written and after following filesystem links. An absolute
244
+ sibling path is accepted only inside that root. Get missing checkouts onto disk, for example with
245
+ `cockpit setup`, before running `sync`.
246
+
247
+ `doctor` and `preflight` inspect every declared environment file in every sibling, and each check
248
+ lists all its failing paths, so one healthy file cannot hide another file's missing instance or
249
+ stale key names. Restore the named checkout or bring its instance keys in line with its template,
250
+ then rerun `cockpit doctor --config cockpit.toml`. When no sibling declares an environment file,
251
+ the instance-presence and key-freshness checks pass: there is nothing to seed or compare.
252
+
253
+ ## Serve a sibling from a worktree
254
+
255
+ The stack mounts each sibling's declared checkout. When you work on a branch in another checkout of
256
+ that repository, usually a linked worktree, point the sibling at it for this cockpit checkout:
257
+
258
+ ```sh
259
+ npx --no cockpit use api /path/to/api/.worktrees/feature --config cockpit.toml
260
+ npx --no cockpit use api --clear --config cockpit.toml
261
+ ```
262
+
263
+ `use` accepts the root of a checkout that shares the declared checkout's object store, or whose
264
+ `origin` names the same repository as the declared checkout or its `remote`; anything else is
265
+ refused with `SIBLING_CHECKOUT_FOREIGN`. Name the checkout's own directory: a path that reaches it
266
+ through a symbolic link is refused. The choice is recorded in `.cockpit/local/sibling-checkouts`,
267
+ never in `cockpit.toml`, so it applies to this checkout of the cockpit alone. `up`, `logs`, the guard's
268
+ allowed roots and protected instance files, `doctor` and `preflight`, work orders (checks, grades,
269
+ test gates and test-weakening detection) and `docs check` all use the recorded checkout, and each
270
+ refuses with `SIBLING_CHECKOUT_UNAVAILABLE` once that checkout is gone. `doctor` and `preflight`
271
+ name each one (`SIBLING_CHECKOUT_OVERRIDDEN`) and warn with
272
+ `SIBLING_ENVIRONMENT_FILE_MISSING` for every declared environment file the worktree lacks: a fresh
273
+ worktree has none of the ignored ones, and cockpit never copies them from another checkout.
274
+
275
+ A running stack keeps the checkout it mounted: run `down` and `up` after `use`. An agent may run
276
+ `use` for a checkout inside a directory it may already write; any other path is a person's call. The
277
+ directory grants `sync` writes for each agent tool are committed and name the declared checkout, so a
278
+ worktree kept inside it stays reachable without a prompt. For a checkout outside it, `use` grants
279
+ the tools that read a machine-local setting, and `use --clear` takes that grant back:
280
+
281
+ - **Claude Code**: `use` adds the checkout to `permissions.additionalDirectories` in
282
+ `.claude/settings.local.json`, keeping everything else in the file, and removes only the entry it
283
+ added. It refuses a file it cannot parse rather than rewrite it, because Claude Code ignores such
284
+ a file entirely.
285
+ - **OpenCode**: `use` writes the checkout to the machine-local grant file `sync` writes for the
286
+ sibling, and `sync` keeps it there.
287
+ - **Codex** has no machine-local setting for extra writable roots, so its sandbox still reaches only
288
+ the declared checkout: start Codex with `--add-dir <checkout>`.
289
+ - **Cursor** and **Grok Build** have no measured machine-local grant, so `use` writes nothing for
290
+ them and they may ask before editing the checkout.
291
+
292
+ `use` lists in its output which tools got the grant, and `--json` reports them as `agentGrants`.
293
+ Restart an agent session that is already running so it reads the new grant.
294
+ [Harness surfaces](harness-surfaces.md#sibling-projection-and-standing-cost) records what was measured.
295
+
296
+ ## Bring checkouts up to date
297
+
298
+ `cockpit pull --config cockpit.toml` fetches and fast-forwards the cockpit and every sibling
299
+ checkout, including one `use` recorded, to the upstream of the branch each has checked out. It
300
+ never merges, rebases, stashes or switches branch, and it runs no Git hooks. A checkout with
301
+ modified tracked files, a branch that has diverged from its upstream, an upstream that is gone or a
302
+ failed fetch is left exactly as it was and reported with `PULL_SKIPPED` and the command that
303
+ resolves it; a detached HEAD or a branch that tracks nothing is listed and skipped without a
304
+ warning. Like `setup`, its Git reads only each repository's own configuration, so a credential
305
+ helper set only in your global Git configuration is not used, and a repository whose attributes name
306
+ a filter defined only there, such as Git LFS, is skipped rather than checked out as placeholder
307
+ files; run `git pull --ff-only` in it yourself. Because it can only fast-forward, an
308
+ agent may run it without asking.
309
+
310
+ ## Lifecycle and ownership
311
+
312
+ `setup` gets the declared sibling checkouts onto disk and wires them up. `mount` controls how the
313
+ stack uses a checkout; `acquire` controls how it gets onto disk; the two are independent. Setup
314
+ looks at declared secret-file paths but never reads or copies their contents.
315
+
316
+ A sibling with `instruction_pointer = true` asks setup to connect the product's own **AGENTS.md**
317
+ to the cockpit guide. Plain `cockpit setup --config cockpit.toml` only reports the proposed path, so
318
+ an agent cannot silently change the instructions later agents load. After a person reviews the
319
+ exact block, `cockpit setup --config cockpit.toml --write-instruction-pointers` appends that marked
320
+ block and keeps every other byte. A missing guide is created only by this approved write; edited,
321
+ partial or duplicate marked blocks are refused so a person can reconcile them. Setup also refuses
322
+ redirected directories and invalid UTF-8 rather than replace the product's bytes.
323
+
324
+ An agent started inside a sibling loads only the instruction files its own tool reads: Codex and
325
+ OpenCode read **AGENTS.md**, while Claude Code reads **CLAUDE.md**, as runs of each tool beside a
326
+ decoy file showed. Every tool gets the same
327
+ instructions only when AGENTS.md holds them and CLAUDE.md imports it with a line reading
328
+ `@AGENTS.md`. `doctor` warns for every sibling laid out otherwise:
329
+ `SIBLING_INSTRUCTIONS_AGENTS_ONLY`, `SIBLING_INSTRUCTIONS_CLAUDE_ONLY`,
330
+ `SIBLING_INSTRUCTIONS_UNLINKED` (both files, no import), `SIBLING_INSTRUCTIONS_MISSING` (neither),
331
+ and `SIBLING_INSTRUCTIONS_TOOL_ONLY` for files such as **.cursorrules** or **GEMINI.md** that none of
332
+ those tools load. Setup proposes the two changes that need no judgement, and makes them only when
333
+ a person runs `cockpit setup --config cockpit.toml --write-instruction-files` after reviewing them:
334
+ a sibling with only AGENTS.md gets a CLAUDE.md holding `@AGENTS.md`, and a sibling with only
335
+ CLAUDE.md has it copied byte for byte to AGENTS.md and replaced by `@AGENTS.md`. Setup proposes no
336
+ move when CLAUDE.md imports other files with `@`, because only Claude Code would follow those
337
+ imports from AGENTS.md. It writes only ordinary files, never through a link, and refuses when a
338
+ file changed after it was read. Merging two diverging files, or writing a first AGENTS.md, needs a
339
+ person's judgement and stays with them.
340
+
341
+ ### Start or resume the stack
342
+
343
+ `up` starts the selected stack, or resumes it. It records a holder it newly adds separately from an
344
+ existing one, so if the start fails, cockpit undoes only what that attempt added. Before resuming,
345
+ cockpit checks that the saved progress still matches the current lifecycle record, runtime and port
346
+ lease. If it does, cockpit rechecks the services that already completed and resumes the rest; if
347
+ anything changed, it discards that progress before touching containers left by the earlier
348
+ attempt. After the first seed, missing saved progress blocks startup. Restore that state rather
349
+ than delete it: deleting it would make the stack look like a fresh environment when it is not.
350
+
351
+ When a service that `up` started does not become healthy, cockpit reports that service's last log
352
+ lines as one diagnostic, then stops its container and keeps it, so a restart policy cannot leave it
353
+ restarting. Failure logs are limited to that service and time window, and never follow the process
354
+ that failed. The holder and lease stay, so the next `up` resumes and `down` removes the stack.
355
+
356
+ When `up` starts services, it times the start, from the earliest recorded service start to the
357
+ moment the last newly started service reports healthy. If that exceeds `cold_start_seconds`, `up`
358
+ refuses with `MACHINE_COLD_START_EXCEEDED`. Each service start may run for the whole declared
359
+ budget, and the configuration refuses a budget above 3600 seconds, the longest `up` holds one
360
+ start open. A successful result reports `coldStart.measuredMillis`,
361
+ `limitMillis` and `headroomMillis`. A partial resume times only the services that run started.
362
+ Joining a stack that is already running, or that another holder started, reports no duration
363
+ rather than an invented one.
364
+
365
+ ### Release the stack
366
+
367
+ `sleep` removes the current holder. It stops the shared runtime only when no other holder remains,
368
+ so one holder cannot stop another's environment. Stopping the runtime also releases the stack's port
369
+ lease, so a stopped stack does not use up one of the `ports.count` families other checkouts need;
370
+ the next `up` leases afresh and may land on a different family, so read the ports and entrypoints
371
+ it returns instead of reusing the earlier ones. `stackId` names the shared runtime and
372
+ `checkoutId` names one checkout; two checkouts with the same stack ID are still not
373
+ interchangeable.
374
+
375
+ `session` runs `up`, keeps that holder while one foreground command runs, and runs `sleep` only
376
+ after confirming that the command's process group has stopped. A command that prints nothing still
377
+ counts as running. Interrupting the wrapper stops only the process group it started, then sleeps.
378
+ If cockpit cannot confirm that the process group stopped, it keeps the holder so you can clean up
379
+ explicitly. Ordinary cockpit lifecycle commands run inside the wrapper use the same private session
380
+ identity, and a second `cockpit session` wrapper is refused. Once cleanup completes, `session`
381
+ exits with the command's own status, or 128 plus the signal number (and names the signal) when a
382
+ signal ended it. It exits 2 when cleanup fails and 130 when you cancel it.
383
+
384
+ A session records its wrapper's process ID and start identity with its hold: on Linux the start in
385
+ clock ticks since boot from `/proc`, which a wall-clock step (NTP, a resumed VM) does not move, and
386
+ elsewhere the start time `ps` reports. A wrapper that dies without its closing `sleep`, from
387
+ SIGKILL, a crash or a closed terminal, leaves that hold behind; the next `up`, `sleep`, `down` or
388
+ `reset` from the checkout sees that the process is gone, or that its process ID now belongs to a
389
+ process with another start, and releases it. When it was the last hold, the command's own hold
390
+ takes the running stack over, so `sleep` stops it and `down` removes it. A hold whose process is
391
+ still running is kept, and so is one whose start could not be read while its process ID is in use:
392
+ the `CLAIM_CO_TENANT` refusal names each such holder and its process ID, so you can end that
393
+ command, for example with `kill <pid>`, and retry.
394
+
395
+ The wrapper passes its process ID and start identity to its command in `COCKPIT_SESSION_PROCESS`, so
396
+ a hold that a `sleep` and then an `up` inside the session drop and re-add is still tied to the
397
+ wrapper, and is released the same way once the wrapper is gone. `up` reads that process's start
398
+ again before recording it, and refuses with `CLI_USAGE_INVALID` when the wrapper has ended or the
399
+ value is malformed.
400
+
401
+ The command `session` runs gets the stack's addresses in its environment, so an agent started under
402
+ it can reach the stack without rerunning `up --json`:
403
+
404
+ - each `[ports.bases]` name holds the port leased for this stack, the same value the Compose files
405
+ read, as in `API_HOST_PORT=4210`;
406
+ - each entrypoint `up --json` returns is under `COCKPIT_ENTRYPOINT_` followed by its name in upper
407
+ case with hyphens as underscores, as in
408
+ `COCKPIT_ENTRYPOINT_API_HOST_PORT=http://127.0.0.1:4210/`.
409
+
410
+ The lease is the truth for the stack, so these replace a value of the same name already set in the
411
+ calling shell. They are read once when the command starts and do not follow a later `down` and
412
+ `up`. When the command ends, the session releases only its own hold: a stack that an earlier `up`
413
+ from this checkout still holds keeps running, and the session says so with
414
+ `SESSION_STACK_RETAINED`; `sleep` releases that hold.
415
+
416
+ Each `sleep` and `down` stops containers with a stop grace: Docker sends SIGTERM and waits ten
417
+ seconds before killing. A service whose PID 1 ignores SIGTERM, as `node` does unless the program
418
+ handles it, pays the whole grace at every stop. Add `init: true` to such a service in your Compose
419
+ file, or handle SIGTERM in the program.
420
+
421
+ ### Remove the stack and its data
422
+
423
+ `down` removes only what cockpit's runtime, checkout and ownership records show it owns: it lists
424
+ only containers, networks and images that carry this stack's ownership label, and reports and
425
+ keeps anything it cannot show is this stack's. Ownership is the stack's identity, not the
426
+ configuration, so a container or network that an earlier `cockpit.toml` started is removed too. An
427
+ image the stack built carries the same label and is removed, unless it also has a second tag.
428
+ Volumes stay, because only `reset` deletes data.
429
+
430
+ When one of the stack's own resources has to stay, such as a network another container is still
431
+ attached to or an image tagged under a second name, `down` refuses with `DOWN_RESOURCES_KEPT` and
432
+ names each resource and the reason, rather than reporting a stack removed that is still there. It
433
+ clears startup progress only after confirming the removals, which lets the next `up` start
434
+ afresh. `down` also retires a stack that `sleep` or an ended `session` left stopped:
435
+ with no holder left, it claims the stack for this checkout without starting anything and removes
436
+ it, so you do not have to run `up` first. While another session still holds the stack, `down`
437
+ refuses with `CLAIM_CO_TENANT` and names that session's holder and process; end that
438
+ `cockpit session`, then retry.
439
+
440
+ `reset` first shows an exact preview of the declared volumes and generated host paths it would
441
+ delete. Plain and `--json` reset calls delete nothing. Interactive reset needs a person at a
442
+ terminal outside CI; the approval stays with the process that showed the preview, which checks
443
+ everything again before it acts. `down` and `reset` delete only from a local Docker daemon, which
444
+ they fix once per run; see [Choose the Docker daemon](#choose-the-docker-daemon).
445
+
446
+ Name each `environment.resettable_volumes` entry by its top-level `volumes:` key in the Compose
447
+ file. Docker's volume name carries a per-checkout project prefix, so it is not accepted. `sync`,
448
+ `check` and every runtime command, `down` included, refuse a name that no declared volume has, and
449
+ an `external` volume too, because cockpit cannot label it. A declared volume that does not exist yet is left out of the
450
+ preview. A volume that an earlier `cockpit.toml` created is still this stack's data, and `reset`
451
+ deletes it with the rest.
452
+
453
+ Every destructive action is recorded durably before it starts. If cockpit itself is interrupted, a
454
+ detached supervisor process keeps track of that exact attempt. The next `sleep`, `down` or approved
455
+ `reset` asks the supervisor to stop the action's process group, confirms the group is gone, marks
456
+ the attempt as failed, and retries the saved action. If the target was already removed, cockpit
457
+ checks again that it is gone and that the remaining targets are unchanged, then marks the action
458
+ complete without repeating it. `DRAIN_ATTEMPT_REPAIR_REQUIRED` means the attempt marker, supervisor
459
+ identity, control socket or stop confirmation did not match; cockpit then keeps the attempt and
460
+ refuses any overlapping cleanup. Do not remove its state or runtime resources by hand: that
461
+ destroys the records cockpit needs to repair it safely.
462
+
463
+ ### List and prune stacks across the machine
464
+
465
+ Each checkout gets its own stack, port lease and state, so deleting a worktree leaves its containers,
466
+ network, volumes and lease behind with no checkout left to run `down` from. `stacks` lists every
467
+ stack this machine knows about, whichever cockpit and checkout started it, and changes nothing:
468
+
469
+ ```sh
470
+ npx --no cockpit stacks --config cockpit.toml --json
471
+ ```
472
+
473
+ It reads cockpit's state root and the containers, networks and volumes on the local Docker daemon
474
+ that carry cockpit's ownership labels, and attributes a resource to a stack only when all of them
475
+ are present and well formed; nothing is attributed by its name. Each row gives the cockpit, the stack,
476
+ its Compose project, its checkout, the state record and whether this version can read it, running
477
+ and stopped containers, networks, volumes, the port lease it still holds, its last recorded
478
+ activity, and a verdict with the reason.
479
+
480
+ A stack's checkout is proven, never guessed. Compose labels each container with the directory it
481
+ ran from, and the ancestor whose checkout identity derives the stack's own identity is its
482
+ checkout; or the cockpit's main checkout is known and none of the checkouts Git records for it
483
+ derives the stack. `checkout.source` says which. The checkout is `present`, `missing`, `replaced`
484
+ (the path exists but is no longer a checkout of that cockpit) or `unknown`. A worktree whose path
485
+ does not exist while Git still registers it, or marks it locked, is `unknown`, not `missing`: a
486
+ drive or share that is not mounted, or a checkout moved without `git worktree repair`, looks exactly
487
+ like that. Once such a checkout is really gone, `git worktree prune` in its main checkout (after
488
+ `git worktree unlock` for a locked one) lets `stacks prune` remove its stack.
489
+
490
+ A stack is `orphaned` only when its checkout is proven `missing`. Every other stack is kept: one
491
+ whose checkout exists or cannot be found, one that a `down` or `reset` is still draining, the
492
+ current checkout's own, and one whose state this cockpit version cannot read, because a newer
493
+ version may still use it for a checkout that exists. A state folder this version cannot read is
494
+ listed with the stacks; one in the colon spelling an earlier build used is orphaned once none of its
495
+ stacks is kept, and one named as current versions name their state is kept for a version that reads
496
+ it.
497
+
498
+ `stacks prune` shows exactly what it would remove and a digest of it, and removes nothing:
499
+
500
+ ```sh
501
+ npx --no cockpit stacks prune --config cockpit.toml
502
+ npx --no cockpit stacks prune --config cockpit.toml --apply
503
+ ```
504
+
505
+ `--apply` observes the machine again and removes only while the plan still has the previewed digest:
506
+ each orphan's containers, then its networks and labelled volumes, then the state records that hold
507
+ its port lease, then the orphaned state folders. State is removed only while every file still holds
508
+ the bytes the preview read, under the lock `up` takes to lease ports. A step that fails stops the
509
+ prune with `STACKS_PRUNE_INCOMPLETE`; what it had not removed stays listed, and running `--apply`
510
+ again resumes. The result is reported only once a fresh inventory no longer lists what was removed.
511
+
512
+ Removing an orphan is housekeeping, so `--apply` runs for an agent. A prune that deletes volumes
513
+ deletes data, and follows the reset gate: it runs only where `[autonomy] reset` lets the caller
514
+ reset, and otherwise a person runs `stacks prune --interactive` in a terminal and types the
515
+ preview's digest. That gate is the running cockpit's own policy and says nothing for another
516
+ cockpit's, so `--apply` removes only this cockpit's orphans and the state folders an earlier build
517
+ left. The preview still lists every orphan, marks each with `thisCockpit`, and warns with
518
+ `STACKS_PRUNE_UNCONFIRMED` that another cockpit's are removed only by a person's
519
+ `stacks prune --interactive`, which covers the whole machine. `doctor` warns with `STACKS_ORPHANED` when a stack of this cockpit has lost its
520
+ checkout or a state record cannot be read, without asking Docker; `stacks` gives the full picture.
521
+
522
+ ### Change a running stack
523
+
524
+ A stack's identity, carried in its ownership labels and saved progress, is a digest of the Compose
525
+ files and of the `cockpit.toml` declarations that reach its containers, not of the bytes of
526
+ `cockpit.toml`. Editing a sibling's
527
+ `purpose`, the guards, MCP, surfaces, comments or formatting leaves a running stack alone, and the
528
+ next `up` resumes it. So do the fields the host checks rather than the containers receive:
529
+ `[environment.machine]` (including `cold_start_seconds`), `addressing`, `authentication`,
530
+ `posture_requirements`, `resettable_volumes`, `seed` and `entrypoint_derivatives`.
531
+
532
+ A change to a Compose file, or to ports, mounts or anything else in `cockpit.toml` the containers
533
+ receive, gives the stack a new identity, and `up` refuses with `LIFECYCLE_PROGRESS_INVALID` rather than replace the
534
+ running stack in place. To apply such a change, for example a `[ports.bases]` edit:
535
+
536
+ 1. Edit `cockpit.toml`. This can happen while the stack runs.
537
+ 2. Run `cockpit sync --config cockpit.toml`. Until then, the agent guard holds every tool call
538
+ except that sync, because the configuration no longer matches what it was generated for, and
539
+ `sleep` and `down` refuse because they read the Compose files `sync` publishes. An agent may run
540
+ it where `[autonomy] sync = "agent"` and the edit does not
541
+ [widen what agents may do](safety-and-autonomy.md#edits-the-guard-has-not-applied), as a
542
+ `[ports.bases]` edit does not; otherwise a person runs it in a terminal.
543
+ 3. Run `cockpit down --config cockpit.toml`. It finds the stack and its port lease by the stack,
544
+ not by the edited declaration, and removes the containers still running the old declaration.
545
+ While another holder, such as a running `cockpit session`, still holds the stack, it refuses
546
+ with `CLAIM_CO_TENANT`.
547
+ 4. Run `cockpit up --config cockpit.toml --json` and use the newly returned ports and entrypoints.
548
+
549
+ Restoring `cockpit.toml` to the exact bytes `sync` last saw, for example with
550
+ `git restore cockpit.toml`, clears the guard's hold without a `sync`: the guard compares the file's
551
+ content, not which file holds it.
552
+
553
+ ### When a sibling changes under a running stack
554
+
555
+ A bind mount follows a sibling's files at once, but installed dependencies usually live in an image
556
+ or volume built earlier and the database keeps the schema it was migrated to. When `up` starts a
557
+ stack it records a digest of each mounted sibling's lockfiles and of its `rebuild_inputs`, such as
558
+ a migrations directory. Lockfiles come from the `environment.install_policies` rows for the
559
+ sibling's toolchain, or, without one, from the common lockfile names present in the checkout; each
560
+ finding names which source it came from. `doctor` and `preflight` warn with `STACK_INPUTS_CHANGED`
561
+ when any of them, or the checkout serving the sibling, changed since, and `pull` warns after
562
+ moving one. Run `down` and then `up` so every service starts on the current files; rebuild an image
563
+ that installs the dependencies before `up`. A resumed `up` keeps the record, because resuming
564
+ restarts nothing; `down`, or a `sleep` that stops the stack, drops it.
565
+
566
+ It is a warning, not a refusal: whether a changed lockfile matters is the product's call, and
567
+ `test-run` does not block on it.
568
+
569
+ ## Choose the Docker daemon
570
+
571
+ Every command uses the daemon Docker itself would select: a `DOCKER_HOST` when set, otherwise the
572
+ context `DOCKER_CONTEXT` names, otherwise the active context `docker context show` reports. Four
573
+ commands fix that daemon once, before their first step, and address only it:
574
+
575
+ | Command | Why it fixes the daemon |
576
+ |---|---|
577
+ | `down`, `reset` | they delete; reading the context per step could list one daemon and delete from another if `docker context use` ran mid-command |
578
+ | `stacks` | `stacks prune` deletes, for the same reason |
579
+ | `runtime measure` | the measurement must describe the daemon `up` will use, not one selected while the probe ran |
580
+
581
+ None of them needs a prefix, so they run as printed:
582
+
583
+ ```sh
584
+ npx --no cockpit down --config cockpit.toml
585
+ npx --no cockpit runtime measure --config cockpit.toml --image node:22-alpine
586
+ ```
587
+
588
+ However the daemon was chosen, its endpoint must be an absolute `unix://` socket path. TCP, SSH and
589
+ other transports are refused, because a remote daemon could receive a deletion and its behaviour
590
+ says nothing about this machine. A `DOCKER_HOST` that is not local is refused with
591
+ `CLI_USAGE_INVALID`; a named or active context that is not local, or whose endpoint cannot be read,
592
+ is refused with `RUNTIME_UNAVAILABLE`, naming the context and its endpoint. For `down` and `reset`,
593
+ conflicting selections are refused, and so is a TLS override. Socket paths keep literal spaces and
594
+ punctuation, and cockpit uses the exact spelling when it binds the context and runs discovery or
595
+ deletion.
596
+
597
+ ## Compose project name
598
+
599
+ Every command that runs Docker Compose passes `--project-name <cockpit.name>-<stack>`, where `<stack>` is the first 12 hex digits of the stack ID. The
600
+ stack ID comes from the cockpit's main checkout and the current checkout, so two cockpits that share
601
+ a `cockpit.name`, or two checkouts of one cockpit, get separate projects on one Docker daemon
602
+ instead of replacing each other's containers. It never depends on the bytes of `cockpit.toml`, so
603
+ editing the file keeps the same project.
604
+
605
+ Containers, networks and named volumes carry that name: a cockpit named `shop-cockpit` runs
606
+ containers such as `shop-cockpit-3f2b3aead733-api-1` and a volume
607
+ `shop-cockpit-3f2b3aead733_postgres-data`. Your own file's `name:` is overridden. To look at the stack with Docker directly, find the project with `docker compose ls`
608
+ and name it: `docker compose --project-name shop-cockpit-3f2b3aead733 ps`. Renaming `cockpit.name`
609
+ addresses a different project and stack, so run `down` before renaming, as
610
+ [Rename a cockpit](getting-started.md#rename-a-cockpit) describes.
611
+
612
+ ## Measure the container runtime
613
+
614
+ `up`, `session`, `down` and `reset` refuse with `RUNTIME_UNMEASURED` unless the container runtime
615
+ Docker selects has been measured on this machine. `sleep` and `logs` act on a running stack and
616
+ fail while its runtime no longer matches a measurement, for example after an engine upgrade.
617
+ `preflight` and `doctor` only report an unmeasured runtime, as an unavailable check. A measurement
618
+ is a fact about one machine, not something the team declares: teammates run different runtimes and
619
+ versions, so it is never written to `cockpit.toml`.
620
+
621
+ cockpit identifies the runtime from one `docker info` reading of `OperatingSystem`, `Name` and
622
+ `OSType`:
623
+
624
+ | Runtime identity | Recognised by | Provenance |
625
+ |---|---|---|
626
+ | `docker-desktop` | `OperatingSystem` is `Docker Desktop` | vendor `docker info` output, not probed |
627
+ | `orbstack` | `OperatingSystem` is `OrbStack` | observed on an OrbStack host |
628
+ | `colima` | `Name` starts with `colima` | vendor `docker info` output, not probed |
629
+ | `rancher-desktop` | `Name` ends with `rancher-desktop` | vendor `docker info` output, not probed |
630
+ | `docker-engine` | a Linux host whose daemon reports `OSType` `linux` | vendor `docker info` output, not probed |
631
+
632
+ Any other reading, including an unrecognised VM on macOS, is not identified and never accepted; the
633
+ refusal lists the raw `docker info` values so you can report them.
634
+
635
+ Measure the runtime on this machine:
636
+
637
+ ```sh
638
+ docker pull node:22-alpine
639
+ ./node_modules/.bin/cockpit runtime measure --config ./cockpit.toml --image node:22-alpine
640
+ ```
641
+
642
+ The command runs a probe in a throwaway directory: a candidate stack that exercises profiles, bind
643
+ mounts in both directions, labels, health, logs, recovery and teardown, next to a deliberately
644
+ broken Compose file that the runtime must reject. It uses only the local image you name and never
645
+ pulls. Its Compose project is `<cockpit.name>-probe-<id>`, so anything it leaves on a shared daemon
646
+ names the cockpit that ran it, and the probe removes it before returning. If a measure was killed
647
+ before it could, the next `runtime measure` first removes that cockpit's own probe containers,
648
+ network and volume and the stale `cockpit-runtime-measure-*` temporary directory, and lists what it
649
+ removed. Each probe resource carries a `dev.b0dea.cockpit.probe-owner` label naming the measure
650
+ process that created it, so a measure running from another worktree or terminal, with its own
651
+ temporary directory, is still recognised: only resources whose process is gone are removed, and
652
+ anything that may belong to a measure still running is left and named. It refuses to probe when a
653
+ leftover cannot be removed, naming it. The probe runs only from
654
+ an installed `@b00dea/cockpit` package and only against a daemon that passes the
655
+ [local endpoint rules](#choose-the-docker-daemon).
656
+
657
+ If the candidate passes and the broken file is rejected, the command records the measurement in
658
+ `machine/runtime-measurements.json` under cockpit's per-user state root (the same root the stack
659
+ state lives in), so every checkout and git worktree on this machine reads it and measures once. The
660
+ record is keyed by the runtime identity and the daemon name `docker info` reports, and holds the
661
+ engine version and the Compose version the probe saw, which becomes the Compose floor. Measuring
662
+ again replaces the row for the same runtime and daemon and keeps the others, so switching between
663
+ two runtimes needs one measurement each. A failed measurement records nothing.
664
+
665
+ `up` admits the runtime only when this machine's record has a row for the same runtime and daemon,
666
+ with the same engine version and a Compose version at or above the floor, so an engine upgrade
667
+ needs a new measurement. Recording a measurement changes no committed file, so an agent can run it
668
+ before its first `up` without waiting for a human `sync`. `doctor` names the measurement that
669
+ admitted the runtime in the `RUNTIME_UNMEASURED` check's source.
670
+
671
+ ## Secret sources and environment schemas
672
+
673
+ Keep each product component's `.env.schema` tracked beside its application. If its ignore rules
674
+ match `.env.*`, explicitly allow `.env.schema` and confirm that Git tracks it before adoption. The
675
+ schema declares variable names, validation and how a provider resolves each value; review its
676
+ resolver code and dependency lock as you would product code.
677
+
678
+ Put encrypted resolver expressions or external-provider references in
679
+ `env/.secrets/<cockpit-project>/<component>`, which the generated guards keep agents from changing
680
+ or staging. Ignore `env/.secrets/` in the cockpit's `.gitignore` before any secret material exists,
681
+ create the directories with mode 0700 and no symbolic links, and create resolver files with mode
682
+ 0600 through your own approved provisioning process. cockpit does not create those files or their
683
+ values.
684
+
685
+ The shipped setup procedure explains how to adopt Varlock, optionally, through the product's
686
+ pinned dependency and an existing declared command or script. Use agent-mode validation for status
687
+ and `run --inject vars --redact-stdout` for a trusted product process, and keep resolved
688
+ credentials out of the agent's environment. Give both paths as absolute paths with repeated
689
+ `--path`: the component schema root first, then its central resolver directory in the cockpit; the
690
+ later path wins. Installation, initialization, provider login, encryption and migration need
691
+ explicit permission from whoever owns the product and the provider. Current commands and
692
+ limitations are in the [Varlock CLI reference](https://varlock.dev/reference/cli/load-and-run/) and
693
+ [AI tools guide](https://varlock.dev/guides/ai-tools/).
694
+
695
+ `cockpit preflight` and `cockpit doctor` check every sibling that contains a `.env.schema` and skip
696
+ the rest. Each runs `varlock load --agent --format json-full --path <sibling>` from your `PATH`
697
+ with the command's timeout and a 256 KiB output cap, and keeps only key names and a validity
698
+ category; values and Varlock's messages are discarded. `ENV_SCHEMA_UNHEALTHY` fails the row and
699
+ names the sibling and its keys, or reports the row as unavailable when Varlock times out, exits
700
+ without a readable report, or a provider cannot resolve a value; run `varlock load` in that sibling
701
+ to see why. `ENV_SCHEMA_TOOL_MISSING` is a warning, not a failure, when Varlock is not installed
702
+ (<https://varlock.dev>).
703
+
704
+ Report only variable names, validation state, exit status and where each value came from. Raw
705
+ resolver output, resolved values and browser state do not belong in work-order results. Generated
706
+ guards refuse changes and staging under secrets directories, including staging a parent directory
707
+ of the central store; whether a harness can natively block reads of these files has not been
708
+ tested. Git ignore rules, filesystem permissions, encryption and output redaction do not protect
709
+ secrets from malicious code running as the same user, and trusted product child processes receive
710
+ plaintext variables. A stronger boundary needs a separately tested sandbox and provider policy.
711
+
712
+ ## Check and sync
713
+
714
+ `check` works out what the generated files should contain now and compares that with every whole
715
+ file and shared-file region listed in the ownership manifest (see
716
+ [generated ownership](concepts.md#cockpit-generated-ownership)). It reports drift and never changes
717
+ an output. `sync --dry-run` does the same preparation and lists the writes and removals, in order,
718
+ without changing anything. `sync` writes the full set as one recoverable filesystem transaction.
719
+
720
+ Team package metadata is prepared separately: a known surface conflict, an input overlap or a
721
+ refused stale-removal approval leaves it unchanged; resolve the reported conflict, then run
722
+ `cockpit sync --config cockpit.toml` again. Metadata is replaced only after these checks pass, but
723
+ a later I/O failure or concurrent input change can still leave those metadata updates in place
724
+ while the generated-file transaction reports its own recovery result.
725
+
726
+ Removing a stale file needs a separate approval of the exact path, operation and current bytes that
727
+ the dry run showed. Plain `sync` refuses to apply it; `sync --interactive` shows the request and
728
+ asks you to type its exact digest. After approval and a final recheck, cockpit can remove an
729
+ unchanged whole file, or its own region in a shared file. Files cockpit did not write,
730
+ and bytes outside its regions, are kept. A stale path that changed or is unsafe needs manual
731
+ reconciliation before you retry.
732
+
733
+ Generated content for a shared Markdown region is rejected before writing if it contains either
734
+ region delimiter, and shared JSON updates keep the whitespace outside cockpit's property group. The
735
+ transaction plans its helper steps before staging and keeps time in reserve to roll back if forward
736
+ work reaches its deadline. If cockpit cannot confirm that writing or rollback finished, it keeps the
737
+ recovery inputs and refuses to report success.
738
+
739
+ Capability states, overlay rules, MCP approval, sibling guide copies, the skill contract and
740
+ `selftest` for the generated harness surfaces are in [Harness surfaces](harness-surfaces.md);
741
+ see [Upgrade cockpit](harness-surfaces.md#upgrade-cockpit) before changing the installed version.
742
+
743
+ ## Bring-up order
744
+
745
+ Bring-up starts from the lifecycle history: `setup` writes `started`, and so does the first `up` on
746
+ a stack that has neither lifecycle data nor history, so you do not have to run `setup` first.
747
+ Lifecycle data without history is refused as lost history. Bring-up then checks the runtime,
748
+ starts the Compose stack, waits for every service to report healthy, records `seeded` in the
749
+ lifecycle history, and only then runs `environment.seed` when it is due. A refusal keeps the
750
+ progress so the next `up` resumes. Fix the named prerequisite or failed check; never edit
751
+ lifecycle state to fake `started` or `seeded` history.
752
+
753
+ ## Result words
754
+
755
+ Environment results use four words with exact meanings. `enforced` and `unavailable` mean what they
756
+ mean for harness features (see [Capability states](harness-surfaces.md#capability-states)); for the
757
+ environment:
758
+
759
+ - **enforced**: cockpit refuses a declaration, file, ownership record, identity, mount or
760
+ operation that does not match.
761
+ - **measured**: a probe produced the stated result. Repository tests run mounted candidates next to
762
+ unmounted and incompatible controls, but those synthetic controls say nothing about your local
763
+ runtime.
764
+ - **unavailable**: cockpit could not get or verify the required result, and it counts as not
765
+ passing.
766
+ - **human_needed**: product or vendor behavior a maintainer still has to review; no check has
767
+ established it.
768
+
769
+ ## Demo data, test users and roles
770
+
771
+ This section is for whoever makes the local stack start with useful data and test users.
772
+ cockpit runs the product's own seed command and records when it last succeeded. It never writes
773
+ rows or creates identities itself: the product's writers already encode its invariants, and a
774
+ second writer in cockpit would drift from them.
775
+
776
+ **What to declare.** Name the Compose volumes that hold the data, and the command that fills them:
777
+
778
+ ```toml
779
+ [environment]
780
+ compose_files = ["compose.yml"]
781
+ resettable_volumes = ["postgres-data"]
782
+ seed = { argv = ["npm", "run", "db:seed"], timeout_seconds = 300 }
783
+ ```
784
+
785
+ `up` runs the seed once every service is healthy, whenever no successful seed is recorded for the
786
+ stack or one of the `resettable_volumes` was created after the last one: the first `up`, the `up`
787
+ after `reset`, and the `up` after anything else removed the volume. The command runs on the host
788
+ from the cockpit directory, with the leased `[ports.bases]` variables and each
789
+ `COCKPIT_ENTRYPOINT_*` address added to the caller's environment, so it reaches the stack as a
790
+ person's shell would. `up --json` reports `seed` as `ran` or `current`. A seed that exits nonzero,
791
+ or runs longer than `timeout_seconds`, refuses with `DATA_SEED_FAILED`: the stack stays up, its data
792
+ may be partly applied, and the next `up` runs the seed again, so write it to be rerun. When Docker
793
+ cannot say when the volumes were created, `up` refuses with `DATA_SEED_UNAVAILABLE` rather than
794
+ guess. `seed` requires `resettable_volumes`, because those volumes are what tells cockpit the data
795
+ is gone. To seed from inside the stack instead, see
796
+ [Compose patterns](compose-patterns.md#seed-or-restore-data).
797
+
798
+ Preview a reset with `cockpit reset`, which lists exactly which volumes and host paths it will remove.
799
+ Applying it wipes data, so it needs a person by default: `reset --interactive` in a terminal, or
800
+ `reset --apply` when `[autonomy] reset` allows the caller. The next `up` seeds again, because the
801
+ volume `reset` recreated is newer than the last seed.
802
+
803
+ ```sh human-only: resets data and requires the declared local stack
804
+ ./node_modules/.bin/cockpit up --config cockpit.toml
805
+ ./node_modules/.bin/cockpit reset --config cockpit.toml --json
806
+ ./node_modules/.bin/cockpit reset --interactive --config cockpit.toml
807
+ ./node_modules/.bin/cockpit up --config cockpit.toml
808
+ ```
809
+
810
+ **Test users.** cockpit has no user model; the product's seed creates them. Give each test user a
811
+ stable synthetic ID such as `auth-admin` or `auth-member`, a role, one operation it must be allowed
812
+ and one it must be denied. Stable IDs let a re-seed update the same rows, and the allowed and denied
813
+ pair proves the roles work, not only that a sign-in succeeded. The shipped `cockpit-setup` skill
814
+ carries an on-demand reference (`references/test-data-and-personas.md`) that keeps these identities
815
+ separate from what an identity provider owns.
816
+
817
+ **Fixture hygiene.** These rules come from seeders that broke in real use:
818
+
819
+ - Seed through the product's own writers, not raw inserts, so derived fields and constraints hold.
820
+ - A re-seed restores only the fields a fixture names. Name every field a test changes, or the next
821
+ run fails its own precondition. Unset fields rather than writing null.
822
+ - Seed the settled state a test needs. Default column values can read as work in flight and
823
+ disable the interface.
824
+ - Before a seed writer runs, assert that every database and service target is local. A development
825
+ fixture is no permission to reach a shared or non-local environment.
826
+ - Resolve a sibling fixture path by probing the declared sibling root and a documented fallback;
827
+ never embed one checkout layout as a constant.
828
+ - Clean up by ownership, never by time window.
829
+
830
+ ## Declare environment postures
831
+
832
+ Declare which local requirements depend on how the product is addressed (`addressing`) and how
833
+ sign-in works locally (`authentication`). For example:
834
+
835
+ ```toml
836
+ [environment]
837
+ addressing = "hostname"
838
+ authentication = "bypassed"
839
+ posture_requirements = [
840
+ { requirement = "local-route-resolution", addressing = "hostname" },
841
+ { requirement = "local-session-access", authentication = "bypassed" },
842
+ ]
843
+ ```
844
+
845
+ A product with no local sign-in declares `authentication = "none"`; no requirement needs a tag for
846
+ it, and a requirement tagged with a sign-in value fails preflight. A plain localhost stack reached
847
+ through leased ports declares:
848
+
849
+ ```toml
850
+ [environment]
851
+ addressing = "port"
852
+ authentication = "none"
853
+ posture_requirements = [{ requirement = "leased-localhost-ports", addressing = "port" }]
854
+ ```
855
+
856
+ Use your actual requirement names. Each tag must match the selected value, and the selected
857
+ `addressing` value, and any `authentication` value other than `none`, must each appear in at least
858
+ one tag. Preflight refuses missing declarations, mismatched tags and selected values that no tag
859
+ uses. Draft configurations may leave these fields out, but then cannot pass that gate. The check
860
+ validates the declared tags; deciding which requirements need tags is still a maintainer's review.
861
+
862
+ ## Health and evidence
863
+
864
+ `preflight` is the blocking gate; `doctor` reports the same checks without repairing anything.
865
+ Before the first bring-up (`STATE_UNSEEDED`), while the stack sleeps
866
+ (`STACK_NOT_RUNNING`), and for a cockpit that declares no runtime (`ENVIRONMENT_NOT_DECLARED`,
867
+ `doctor` only), both still run every check that needs no running stack, such as Git state, sibling
868
+ presence, the machine floor and the runtime measurement, and report the checks that need running
869
+ services as not applicable; `preflight` never passes in those states. A check can pass, fail, be
870
+ unavailable, or not apply to the declared setup, and an unavailable required check never becomes a
871
+ pass because another check succeeded. Host capacity and runtime health need complete results from processes cockpit itself ran; when those results are missing, the
872
+ result does not pass.
873
+
874
+ `preflight` and `doctor` read current results without changing anything; `doctor` also previews
875
+ generated surfaces to list overrides. They never run `sync`, seed data or advance lifecycle
876
+ progress. Only bring-up runs those steps, as [Bring-up order](#bring-up-order) describes.
877
+
878
+ `doctor` exits 0 with a report whenever it can read `cockpit.toml`, whatever the verdict, and exits
879
+ 2 when the configuration is missing or invalid. Its human output is one line per area followed by
880
+ the diagnostics; `--json` carries the checks, declarations, override inspection and whether the
881
+ generated files are current. `check --json` shows the generated-file detail.
882
+
883
+ What the checks compare:
884
+
885
+ - **Git remotes.** A declared sibling remote is compared with the remote selected by that
886
+ checkout's branch upstream; cockpit does not rewrite Git remotes. A checkout with no upstream has
887
+ nothing to compare, and the result says so. Once an upstream is selected, it must be available
888
+ and match; a missing or diverged upstream blocks the gate. Only an https or ssh `remote` has a
889
+ repository identity to compare. A `remote` that is a local path (not a URL or `host:path`) is
890
+ still cloned by `setup`, resolved against the siblings root, but `preflight` refuses it with a
891
+ reason: declare the hosted https or ssh remote, or remove `remote` and set `acquire = "none"`.
892
+ `init` never proposes a local-path remote.
893
+ - **Surfaces.** Generated files must match the ownership manifest and stay within the guide line
894
+ cap and the built-in harness skill caps. Custom outputs and plugin bundle copies are checked for
895
+ being current, but no harness-side cap on their always-loaded size applies to them. Whether each
896
+ harness enforces the files is recorded in the [capability matrix](capability-matrix.md). Missing
897
+ or edited generated files block health: run `cockpit sync --config cockpit.toml`, then retry.
898
+ - **Grants.** Directory grants are checked only when a sibling declares `mount = "always"`.
899
+ - **Mounts and ports.** A runtime with no bind mounts passes as an exact empty mount comparison;
900
+ declared binds must match the current mounts exactly. Health on a running stack rechecks the
901
+ retained lease, current container identities, mounts and published listeners without reserving
902
+ sockets. Compose reports a `::` publication beside each `0.0.0.0` one, and some runtimes forward
903
+ only IPv4 loopback, so the `::` row is not required to listen while its `0.0.0.0` twin does; a
904
+ leased port that nothing publishes must stay free on both address families.
905
+ - **Capacity.** Cold-start timing applies only to services that `up` actually started; read-only
906
+ health checks do not invent a new duration. The machine disk floor compares current free space
907
+ with an explicitly declared machine floor, independently of generated file sizes; without that
908
+ declaration, host-floor checks do not apply.
909
+
910
+ `logs` reads one declared service from the selected running stack. `--tail` accepts 1 through 10000
911
+ records, and `--since` limits the time range. Records keep their stream and restart details. A read
912
+ without `--follow` has ten seconds and reports a timeout as a failure. `--follow` collects until you
913
+ interrupt it, the container stops, or two hours pass; interrupting it in the CLI returns exit 130
914
+ with the records collected before the process group stopped, and programmatic callers must supply an
915
+ abort signal. Output is limited to 1 MiB of the container's stdout and 64 KiB of its stderr. Past
916
+ that, `logs` keeps the newest whole records that fit, sets `truncated` to `true` in the JSON result
917
+ and warns with `LOGS_TRUNCATED`; lower `--tail` to stay under it. Output is returned after
918
+ collection, including structured records in JSON mode, rather than streamed.
919
+
920
+ ### Work on one service
921
+
922
+ `ps`, `restart`, `rebuild` and `exec` act on one service of this checkout's own running stack, with
923
+ its Compose project, files and profiles, so neither a person nor an agent names the derived project.
924
+ The stack must have been started by `up` from this checkout; after `down` they refuse and name `up`.
925
+
926
+ - `ps` lists every service of the stack with its container state, health and status; a service
927
+ with no container is `absent`.
928
+ - `restart --service <name>` restarts the container in place, without its dependents, then waits
929
+ for it as `up` does and reports its last log lines when it does not become healthy.
930
+ - `rebuild --service <name>` builds the service's image, replaces its container and waits the same
931
+ way; use it when a change is baked into the image. It also works on a stack whose start stopped at
932
+ that service, and it records the new container so `preflight` and `logs` follow it.
933
+ - `exec --service <name> -- <command> [args...]` runs the command inside the service's container
934
+ and exits with the command's own status, as `session` does. It gets a terminal only when cockpit
935
+ runs in one.
936
+
937
+ The guard admits these verbs as it admits `up`. The command after `exec --` runs in the container,
938
+ whose files the guard cannot see, so it is never proven read-only: the name-based denials still apply
939
+ (`exec --service web -- cat .env` is refused like `cat .env`), and it runs without asking only when a
940
+ `[guards] allow` rule or a declared build or test command admits it
941
+ ([Cockpit and project commands](safety-and-autonomy.md#cockpit-and-project-commands)). `reset` stays
942
+ gated by `[autonomy]`.
943
+
944
+ ## Where the decisions live
945
+
946
+ Contributors looking for the source module behind each step should start at
947
+ [Architecture](architecture.md#runtime-behavior) and the
948
+ [environment code map](https://github.com/b0dea/cockpit/blob/main/AGENTS.md#environment-code-map).