agent-containers 0.1.0.dev0__tar.gz

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 (86) hide show
  1. agent_containers-0.1.0.dev0/LICENSE +21 -0
  2. agent_containers-0.1.0.dev0/PKG-INFO +195 -0
  3. agent_containers-0.1.0.dev0/README.md +176 -0
  4. agent_containers-0.1.0.dev0/pyproject.toml +108 -0
  5. agent_containers-0.1.0.dev0/pyproject.toml.orig +86 -0
  6. agent_containers-0.1.0.dev0/src/agent_containers/__init__.py +1 -0
  7. agent_containers-0.1.0.dev0/src/agent_containers/__main__.py +6 -0
  8. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/Dockerfile +103 -0
  9. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/examples/egress-allowlist.txt +10 -0
  10. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/packages-apt.txt +13 -0
  11. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/packages-npm.txt +2 -0
  12. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/packages-uv.txt +6 -0
  13. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/tools-uv.txt +4 -0
  14. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/agent-gateway/Dockerfile +50 -0
  15. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/agent-gateway/entrypoint.sh +28 -0
  16. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/agent-gateway/sshd_config.agent-gateway +11 -0
  17. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/Dockerfile +114 -0
  18. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/examples/egress-allowlist.txt +16 -0
  19. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/packages-apt.txt +16 -0
  20. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/packages-npm.txt +7 -0
  21. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/packages-uv.txt +9 -0
  22. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/tools-uv.txt +9 -0
  23. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/Dockerfile +171 -0
  24. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/entrypoint.sh +22 -0
  25. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/examples/egress-allowlist.txt +14 -0
  26. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/examples/settings.local-model.json +7 -0
  27. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/packages-apt.txt +16 -0
  28. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/packages-npm.txt +7 -0
  29. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/packages-uv.txt +9 -0
  30. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/plugin-marketplaces.txt +23 -0
  31. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/plugins.txt +13 -0
  32. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/proxy-ca.d/.keep +1 -0
  33. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/tools-uv.txt +9 -0
  34. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/Dockerfile +147 -0
  35. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/codex-wrapper.sh +13 -0
  36. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/examples/egress-allowlist.txt +10 -0
  37. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/packages-apt.txt +13 -0
  38. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/packages-npm.txt +2 -0
  39. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/packages-uv.txt +6 -0
  40. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/plugin-marketplaces.txt +2 -0
  41. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/plugins.txt +2 -0
  42. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/proxy-ca.d/.keep +1 -0
  43. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/tools-uv.txt +4 -0
  44. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/Dockerfile +122 -0
  45. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/entrypoint.sh +49 -0
  46. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/packages-apt.txt +16 -0
  47. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/packages-npm.txt +7 -0
  48. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/packages-uv.txt +9 -0
  49. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/proxy-ca.d/.keep +1 -0
  50. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/tools-uv.txt +9 -0
  51. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/Dockerfile +101 -0
  52. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/examples/egress-allowlist.txt +7 -0
  53. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/packages-apt.txt +5 -0
  54. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/packages-npm.txt +1 -0
  55. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/packages-uv.txt +4 -0
  56. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/tools-uv.txt +4 -0
  57. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/Dockerfile +126 -0
  58. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/examples/egress-allowlist.txt +6 -0
  59. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/packages-apt.txt +13 -0
  60. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/packages-npm.txt +2 -0
  61. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/packages-uv.txt +6 -0
  62. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/proxy-ca.d/.keep +1 -0
  63. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/tools-uv.txt +4 -0
  64. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/Dockerfile +104 -0
  65. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/examples/egress-allowlist.txt +10 -0
  66. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/packages-apt.txt +13 -0
  67. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/packages-npm.txt +2 -0
  68. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/packages-uv.txt +6 -0
  69. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/tools-uv.txt +4 -0
  70. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/egress-allowlist.sh +138 -0
  71. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/install-additional-packages.sh +79 -0
  72. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/install-workload-base.sh +46 -0
  73. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/workload-entrypoint.sh +82 -0
  74. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/volume-bridge/Dockerfile +37 -0
  75. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/volume-bridge/entrypoint.sh +29 -0
  76. agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/volume-bridge/serve.sh +66 -0
  77. agent_containers-0.1.0.dev0/src/agent_containers/assets.py +38 -0
  78. agent_containers-0.1.0.dev0/src/agent_containers/build_context.py +116 -0
  79. agent_containers-0.1.0.dev0/src/agent_containers/cli.py +212 -0
  80. agent_containers-0.1.0.dev0/src/agent_containers/creation.py +177 -0
  81. agent_containers-0.1.0.dev0/src/agent_containers/docker.py +562 -0
  82. agent_containers-0.1.0.dev0/src/agent_containers/lifecycle.py +325 -0
  83. agent_containers-0.1.0.dev0/src/agent_containers/planner.py +99 -0
  84. agent_containers-0.1.0.dev0/src/agent_containers/profile.py +377 -0
  85. agent_containers-0.1.0.dev0/src/agent_containers/shortcuts.py +159 -0
  86. agent_containers-0.1.0.dev0/src/agent_containers/state.py +154 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anirban Basu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-containers
3
+ Version: 0.1.0.dev0
4
+ Summary: Deterministic onboarding and maintenance for hardened coding-agent containers
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Classifier: Development Status :: 2 - Pre-Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Requires-Dist: pydantic>=2.11,<3
12
+ Requires-Dist: tomlkit>=0.13,<1
13
+ Requires-Dist: typer>=0.16,<1
14
+ Requires-Python: >=3.12
15
+ Project-URL: Repository, https://github.com/anirbanbasu/agent-containers
16
+ Project-URL: Issues, https://github.com/anirbanbasu/agent-containers/issues
17
+ Project-URL: Download, https://pypi.org/project/agent-containers/
18
+ Description-Content-Type: text/markdown
19
+
20
+ # agent-containers CLI
21
+
22
+ The Python package is tested on Python 3.14 (with Python 3.12+ support). It
23
+ validates profiles, produces offline plans, materializes bundled image recipes,
24
+ and applies a direct-Docker deployment without launching an agent session.
25
+
26
+ This directory will contain the standalone Python project for deterministic
27
+ onboarding and maintenance of this repository's hardened agent images. The
28
+ distribution and executable name is `agent-containers`; the Python import
29
+ package will be `agent_containers`.
30
+
31
+ ## Agreed scope
32
+
33
+ - Human-editable, versioned TOML profiles, with separate machine-managed
34
+ deployment state. Interactive prompts and noninteractive inputs use the same
35
+ validation model; unknown fields are errors.
36
+ - Claude Code, OpenCode, Codex, and Hermes adapters, targeting Linux Docker Engine
37
+ and macOS Docker Desktop. Support claims require actual integration tests.
38
+ - All four optional package categories: apt packages, npm packages, isolated uv
39
+ tools, and system Python libraries installed with uv.
40
+ - Explicit custom file/directory mounts and copy-once configuration seeds,
41
+ without duplicating each agent's settings schema. Relative input paths resolve
42
+ against the profile directory; the workspace defaults to the launch directory.
43
+ - Composable hosted/local/custom model endpoints, HTTP(S) proxies, CA trust,
44
+ egress gateways, and experimental observability integrations. Decant is
45
+ experimental and currently limited to Claude Code and Codex; it is disabled
46
+ unless a profile opts in. Opted-in Decant launches use user-scoped image,
47
+ container, and data-volume names and mount selected agent collection
48
+ subdirectories directly; the delegated volume bridge remains a separate
49
+ manual setup. Local inference engine installation and model
50
+ downloads are outside the agreed initial scope.
51
+ - Explicit application and updates, retained prior deployments, and rollback of
52
+ the selected image and managed launch configuration. Rollback does not restore
53
+ mutable home-volume data or terminate existing sessions automatically.
54
+ - Image recipes bundled from canonical `../agent-images/` sources in both wheel
55
+ and source distributions. No maintained duplicate or installed-package edits.
56
+
57
+ ## Safety and verification
58
+
59
+ Preserve the repository's containment contract. Package selection must not
60
+ silently expand runtime egress. Credentials must not appear in profiles, build
61
+ arguments, images, logs, or command output; profiles reference runtime inputs.
62
+
63
+ When a profile declares a proxy, its generated Docker launch exports both
64
+ uppercase and lowercase `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` variables,
65
+ and enables Node's environment proxy support. A profile `proxy.ca_file` or
66
+ `proxy.ca_dir` is copied into the isolated image build context and installed
67
+ into the image's system trust store. The generated launch points
68
+ `SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, and `NODE_EXTRA_CA_CERTS` at the merged
69
+ system bundle. Certificate inputs are never placed in the shared canonical
70
+ image assets; they are deployment-specific and excluded from command output.
71
+
72
+ Gateway profiles require a host and port plus a read-only SSH key and pinned
73
+ known-hosts input. These can be supplied through the dedicated profile paths or
74
+ explicit mounts at `/etc/agent/gateway-key` and
75
+ `/etc/agent/gateway-known-hosts`; bootstrap rules are restricted to literal
76
+ IP addresses/CIDRs. Gateway user, Cloudflare Access hostname, and bootstrap
77
+ allow rules are emitted only when gateway mode is explicitly configured.
78
+
79
+ Langfuse support is experimental and opt-in for Claude Code, Codex, and
80
+ OpenCode. The CLI installs the vendor-supported integration for the selected
81
+ agent and emits only references to credential environment variables; it never
82
+ stores credential values. A Langfuse endpoint must be present in the profile's
83
+ egress allowlist (or be reached through a configured gateway). Langfuse is one
84
+ of many possible OpenTelemetry backends, not a default telemetry provider.
85
+
86
+ Unit and distribution tests must run without Docker. Integration tests require
87
+ Docker and disposable resources, without real account logins. Offline plans and
88
+ diagnostics must distinguish recorded state from inspected live state and mark
89
+ runtime checks unavailable when Docker cannot be reached.
90
+
91
+ Use direct Docker commands, not Compose, and package with `uv build` and the
92
+ native `uv_build` backend, not Hatchling, with Python 3.12 or newer. Use `ty`
93
+ for type checking and Ruff for linting/formatting. Both live in the `dev`
94
+ dependency group; pytest and coverage live in `test`.
95
+
96
+ From this directory, install contributor dependencies with
97
+ `uv sync --group test --python 3.12`. Root `just test-cli` runs unit and packaging
98
+ tests under coverage; `just check-cli` runs Ruff and ty. Coverage must reach 100%.
99
+ Do not suppress type or coverage errors without a demonstrated, documented need.
100
+ The packaging test builds a standalone source distribution and installs its
101
+ rebuilt wheel in a fresh temporary environment; dependency downloads may be needed.
102
+
103
+ The CLI prints its required ASCII banner before every invocation. Deployment
104
+ state defaults to `$XDG_STATE_HOME/agent-containers/` (or
105
+ `~/.local/state/agent-containers/`). On macOS Docker Desktop, `apply` keeps the
106
+ host UID but uses image GID `1000`: the conventional macOS GID 20 collides with
107
+ an existing Linux image group, and the project deliberately retains its
108
+ Dockerfile collision rejection rather than silently joining that group.
109
+
110
+ If the host mounts `/tmp` with `noexec`, use a dedicated executable temporary
111
+ directory for pytest's `--basetemp` option. Pytest owns and clears that directory:
112
+ never point it at an existing directory containing your files. No mount flags
113
+ need changing. Tests still install outside the repository and use a fresh venv.
114
+
115
+ The test suite covers CLI behavior, profile/state validation, generated build
116
+ contexts, Docker argv construction, lifecycle transitions, and standalone
117
+ sdist-to-wheel installation at 100% source coverage. It does not establish all
118
+ runtime containment or agent compatibility claims; broader Docker integration
119
+ tests remain to be implemented.
120
+
121
+ Profile-managed npm and uv tools are installed into image-owned locations
122
+ outside the persistent home during image creation only. Runtime root filesystems
123
+ stay read-only, with the existing explicit writable mounts preserved. Manual
124
+ home installations take PATH precedence; diagnostics must warn when they shadow
125
+ image-managed executables. Do not delete existing home tools.
126
+
127
+ The private implementation handoff is maintained in
128
+ `../tmp-onboarding-codex.log`; the original discussion is in
129
+ `../tmp-onboarding.md`. Both are intentionally ignored by Git.
130
+
131
+ ## Current commands
132
+
133
+ `agent-containers create PROFILE.toml` interactively prompts for every profile
134
+ section, validates the resulting model, and writes a new TOML file atomically.
135
+ It refuses to replace an existing file and never contacts Docker. Secrets are
136
+ never prompted for; provider authentication is represented only by an
137
+ environment-variable name.
138
+
139
+ `agent-containers validate PROFILE.toml` parses and validates a profile without
140
+ contacting Docker or changing files.
141
+
142
+ `agent-containers plan PROFILE.toml` compares the desired profile with optional
143
+ recorded deployment JSON (`--state STATE.json`). It reports image versus launch
144
+ changes and always discloses that live Docker state was not inspected.
145
+
146
+ `agent-containers apply PROFILE.toml` materializes an isolated profile-owned
147
+ build context, builds or verifies its retained image, copies any new seed only
148
+ when its target is absent, and atomically selects deployment state. State is
149
+ written to the standard per-user location unless `--state STATE.json` overrides
150
+ it. `apply` does not launch an agent or terminate an existing session. After a
151
+ successful apply it also refreshes the CLI-managed
152
+ `$XDG_CONFIG_HOME/agent-containers/profiles.sh` (or
153
+ `~/.config/agent-containers/profiles.sh`) with a namespaced shortcut for the
154
+ profile. Source that file once from your shell startup file:
155
+
156
+ ```sh
157
+ source "$HOME/.config/agent-containers/profiles.sh"
158
+ ```
159
+
160
+ For example, a profile named `work-codex` becomes
161
+ `agent_containers_work_codex`. The generated function evaluates the current
162
+ directory when invoked, selects the profile's retained image and home volume,
163
+ and forwards arguments to the agent. It does not accept arbitrary Docker
164
+ options; use the documented generic shortcuts when an intentional one-off
165
+ override is needed.
166
+
167
+ By default, profile resources are namespaced with the host username and UID:
168
+ an image tag and home volume created for `work` by one user cannot collide with
169
+ the corresponding `work` resources created by another user on the same Docker
170
+ host. Set `home_volume = "an-existing-volume"` in the profile when an existing
171
+ named volume should be reused; that explicit name is an intentional sharing
172
+ choice and is never silently renamed.
173
+
174
+ Provider launch mappings are agent-specific: Claude Code uses its endpoint and
175
+ model environment variables, Codex uses per-run configuration overrides, and
176
+ Hermes uses per-run `--provider`/`--model` flags plus the documented base-URL
177
+ environment for supported `custom`, `openai`, and `anthropic` routes. OpenCode
178
+ endpoint/model fields are rendered into a secret-free JSON provider
179
+ configuration for that invocation. The shared entrypoint writes it under
180
+ `/tmp` and sets `OPENCODE_CONFIG`, so the profile does not modify the
181
+ persistent OpenCode home.
182
+
183
+ `agent-containers rollback PROFILE.toml` verifies and selects the immediately
184
+ previous retained image and launch record. It previews restored egress,
185
+ proxy/CA, and endpoint choices. It never restores persistent-home data or stops
186
+ an existing agent session, and refreshes that profile's generated shortcut.
187
+
188
+ `agent-containers doctor PROFILE.toml` is read-only. It reports Docker-daemon
189
+ availability, the expected deployment-state file, and whether the selected
190
+ image can be inspected; it creates, starts, and changes nothing.
191
+
192
+ Before building a distribution, run `uv run --project cli python cli/scripts/bundle_image_assets.py`;
193
+ `just build-cli` does this automatically.
194
+ The generated package-data directory is ignored, derived only from canonical
195
+ `agent-images/`, and included in the wheel and source distribution.
@@ -0,0 +1,176 @@
1
+ # agent-containers CLI
2
+
3
+ The Python package is tested on Python 3.14 (with Python 3.12+ support). It
4
+ validates profiles, produces offline plans, materializes bundled image recipes,
5
+ and applies a direct-Docker deployment without launching an agent session.
6
+
7
+ This directory will contain the standalone Python project for deterministic
8
+ onboarding and maintenance of this repository's hardened agent images. The
9
+ distribution and executable name is `agent-containers`; the Python import
10
+ package will be `agent_containers`.
11
+
12
+ ## Agreed scope
13
+
14
+ - Human-editable, versioned TOML profiles, with separate machine-managed
15
+ deployment state. Interactive prompts and noninteractive inputs use the same
16
+ validation model; unknown fields are errors.
17
+ - Claude Code, OpenCode, Codex, and Hermes adapters, targeting Linux Docker Engine
18
+ and macOS Docker Desktop. Support claims require actual integration tests.
19
+ - All four optional package categories: apt packages, npm packages, isolated uv
20
+ tools, and system Python libraries installed with uv.
21
+ - Explicit custom file/directory mounts and copy-once configuration seeds,
22
+ without duplicating each agent's settings schema. Relative input paths resolve
23
+ against the profile directory; the workspace defaults to the launch directory.
24
+ - Composable hosted/local/custom model endpoints, HTTP(S) proxies, CA trust,
25
+ egress gateways, and experimental observability integrations. Decant is
26
+ experimental and currently limited to Claude Code and Codex; it is disabled
27
+ unless a profile opts in. Opted-in Decant launches use user-scoped image,
28
+ container, and data-volume names and mount selected agent collection
29
+ subdirectories directly; the delegated volume bridge remains a separate
30
+ manual setup. Local inference engine installation and model
31
+ downloads are outside the agreed initial scope.
32
+ - Explicit application and updates, retained prior deployments, and rollback of
33
+ the selected image and managed launch configuration. Rollback does not restore
34
+ mutable home-volume data or terminate existing sessions automatically.
35
+ - Image recipes bundled from canonical `../agent-images/` sources in both wheel
36
+ and source distributions. No maintained duplicate or installed-package edits.
37
+
38
+ ## Safety and verification
39
+
40
+ Preserve the repository's containment contract. Package selection must not
41
+ silently expand runtime egress. Credentials must not appear in profiles, build
42
+ arguments, images, logs, or command output; profiles reference runtime inputs.
43
+
44
+ When a profile declares a proxy, its generated Docker launch exports both
45
+ uppercase and lowercase `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` variables,
46
+ and enables Node's environment proxy support. A profile `proxy.ca_file` or
47
+ `proxy.ca_dir` is copied into the isolated image build context and installed
48
+ into the image's system trust store. The generated launch points
49
+ `SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, and `NODE_EXTRA_CA_CERTS` at the merged
50
+ system bundle. Certificate inputs are never placed in the shared canonical
51
+ image assets; they are deployment-specific and excluded from command output.
52
+
53
+ Gateway profiles require a host and port plus a read-only SSH key and pinned
54
+ known-hosts input. These can be supplied through the dedicated profile paths or
55
+ explicit mounts at `/etc/agent/gateway-key` and
56
+ `/etc/agent/gateway-known-hosts`; bootstrap rules are restricted to literal
57
+ IP addresses/CIDRs. Gateway user, Cloudflare Access hostname, and bootstrap
58
+ allow rules are emitted only when gateway mode is explicitly configured.
59
+
60
+ Langfuse support is experimental and opt-in for Claude Code, Codex, and
61
+ OpenCode. The CLI installs the vendor-supported integration for the selected
62
+ agent and emits only references to credential environment variables; it never
63
+ stores credential values. A Langfuse endpoint must be present in the profile's
64
+ egress allowlist (or be reached through a configured gateway). Langfuse is one
65
+ of many possible OpenTelemetry backends, not a default telemetry provider.
66
+
67
+ Unit and distribution tests must run without Docker. Integration tests require
68
+ Docker and disposable resources, without real account logins. Offline plans and
69
+ diagnostics must distinguish recorded state from inspected live state and mark
70
+ runtime checks unavailable when Docker cannot be reached.
71
+
72
+ Use direct Docker commands, not Compose, and package with `uv build` and the
73
+ native `uv_build` backend, not Hatchling, with Python 3.12 or newer. Use `ty`
74
+ for type checking and Ruff for linting/formatting. Both live in the `dev`
75
+ dependency group; pytest and coverage live in `test`.
76
+
77
+ From this directory, install contributor dependencies with
78
+ `uv sync --group test --python 3.12`. Root `just test-cli` runs unit and packaging
79
+ tests under coverage; `just check-cli` runs Ruff and ty. Coverage must reach 100%.
80
+ Do not suppress type or coverage errors without a demonstrated, documented need.
81
+ The packaging test builds a standalone source distribution and installs its
82
+ rebuilt wheel in a fresh temporary environment; dependency downloads may be needed.
83
+
84
+ The CLI prints its required ASCII banner before every invocation. Deployment
85
+ state defaults to `$XDG_STATE_HOME/agent-containers/` (or
86
+ `~/.local/state/agent-containers/`). On macOS Docker Desktop, `apply` keeps the
87
+ host UID but uses image GID `1000`: the conventional macOS GID 20 collides with
88
+ an existing Linux image group, and the project deliberately retains its
89
+ Dockerfile collision rejection rather than silently joining that group.
90
+
91
+ If the host mounts `/tmp` with `noexec`, use a dedicated executable temporary
92
+ directory for pytest's `--basetemp` option. Pytest owns and clears that directory:
93
+ never point it at an existing directory containing your files. No mount flags
94
+ need changing. Tests still install outside the repository and use a fresh venv.
95
+
96
+ The test suite covers CLI behavior, profile/state validation, generated build
97
+ contexts, Docker argv construction, lifecycle transitions, and standalone
98
+ sdist-to-wheel installation at 100% source coverage. It does not establish all
99
+ runtime containment or agent compatibility claims; broader Docker integration
100
+ tests remain to be implemented.
101
+
102
+ Profile-managed npm and uv tools are installed into image-owned locations
103
+ outside the persistent home during image creation only. Runtime root filesystems
104
+ stay read-only, with the existing explicit writable mounts preserved. Manual
105
+ home installations take PATH precedence; diagnostics must warn when they shadow
106
+ image-managed executables. Do not delete existing home tools.
107
+
108
+ The private implementation handoff is maintained in
109
+ `../tmp-onboarding-codex.log`; the original discussion is in
110
+ `../tmp-onboarding.md`. Both are intentionally ignored by Git.
111
+
112
+ ## Current commands
113
+
114
+ `agent-containers create PROFILE.toml` interactively prompts for every profile
115
+ section, validates the resulting model, and writes a new TOML file atomically.
116
+ It refuses to replace an existing file and never contacts Docker. Secrets are
117
+ never prompted for; provider authentication is represented only by an
118
+ environment-variable name.
119
+
120
+ `agent-containers validate PROFILE.toml` parses and validates a profile without
121
+ contacting Docker or changing files.
122
+
123
+ `agent-containers plan PROFILE.toml` compares the desired profile with optional
124
+ recorded deployment JSON (`--state STATE.json`). It reports image versus launch
125
+ changes and always discloses that live Docker state was not inspected.
126
+
127
+ `agent-containers apply PROFILE.toml` materializes an isolated profile-owned
128
+ build context, builds or verifies its retained image, copies any new seed only
129
+ when its target is absent, and atomically selects deployment state. State is
130
+ written to the standard per-user location unless `--state STATE.json` overrides
131
+ it. `apply` does not launch an agent or terminate an existing session. After a
132
+ successful apply it also refreshes the CLI-managed
133
+ `$XDG_CONFIG_HOME/agent-containers/profiles.sh` (or
134
+ `~/.config/agent-containers/profiles.sh`) with a namespaced shortcut for the
135
+ profile. Source that file once from your shell startup file:
136
+
137
+ ```sh
138
+ source "$HOME/.config/agent-containers/profiles.sh"
139
+ ```
140
+
141
+ For example, a profile named `work-codex` becomes
142
+ `agent_containers_work_codex`. The generated function evaluates the current
143
+ directory when invoked, selects the profile's retained image and home volume,
144
+ and forwards arguments to the agent. It does not accept arbitrary Docker
145
+ options; use the documented generic shortcuts when an intentional one-off
146
+ override is needed.
147
+
148
+ By default, profile resources are namespaced with the host username and UID:
149
+ an image tag and home volume created for `work` by one user cannot collide with
150
+ the corresponding `work` resources created by another user on the same Docker
151
+ host. Set `home_volume = "an-existing-volume"` in the profile when an existing
152
+ named volume should be reused; that explicit name is an intentional sharing
153
+ choice and is never silently renamed.
154
+
155
+ Provider launch mappings are agent-specific: Claude Code uses its endpoint and
156
+ model environment variables, Codex uses per-run configuration overrides, and
157
+ Hermes uses per-run `--provider`/`--model` flags plus the documented base-URL
158
+ environment for supported `custom`, `openai`, and `anthropic` routes. OpenCode
159
+ endpoint/model fields are rendered into a secret-free JSON provider
160
+ configuration for that invocation. The shared entrypoint writes it under
161
+ `/tmp` and sets `OPENCODE_CONFIG`, so the profile does not modify the
162
+ persistent OpenCode home.
163
+
164
+ `agent-containers rollback PROFILE.toml` verifies and selects the immediately
165
+ previous retained image and launch record. It previews restored egress,
166
+ proxy/CA, and endpoint choices. It never restores persistent-home data or stops
167
+ an existing agent session, and refreshes that profile's generated shortcut.
168
+
169
+ `agent-containers doctor PROFILE.toml` is read-only. It reports Docker-daemon
170
+ availability, the expected deployment-state file, and whether the selected
171
+ image can be inspected; it creates, starts, and changes nothing.
172
+
173
+ Before building a distribution, run `uv run --project cli python cli/scripts/bundle_image_assets.py`;
174
+ `just build-cli` does this automatically.
175
+ The generated package-data directory is ignored, derived only from canonical
176
+ `agent-images/`, and included in the wheel and source distribution.
@@ -0,0 +1,108 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.12.10,<0.13"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "agent-containers"
7
+ version = "0.1.0.dev0"
8
+ description = "Deterministic onboarding and maintenance for hardened coding-agent containers"
9
+ license = "MIT"
10
+ license-files = ["LICEN[CS]E*"]
11
+ readme = "README.md"
12
+ requires-python = ">=3.12"
13
+ dependencies = [
14
+ "pydantic>=2.11,<3",
15
+ "tomlkit>=0.13,<1",
16
+ "typer>=0.16,<1",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 2 - Pre-Alpha",
20
+ "Intended Audience :: Developers",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.12",
23
+ ]
24
+
25
+ [project.urls]
26
+ Repository = "https://github.com/anirbanbasu/agent-containers"
27
+ Issues = "https://github.com/anirbanbasu/agent-containers/issues"
28
+ Download = "https://pypi.org/project/agent-containers/"
29
+
30
+ [project.scripts]
31
+ agent-containers = "agent_containers.cli:app"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "ty>=0.0.1,<1",
36
+ "ruff>=0.11,<1",
37
+ ]
38
+ test = [
39
+ "pytest>=8.4.1,<10",
40
+ "coverage>=7.9.1,<8",
41
+ ]
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
45
+ addopts = [
46
+ "--import-mode=importlib",
47
+ "--strict-markers",
48
+ ]
49
+ markers = [
50
+ "packaging: builds and installs distributions without Docker",
51
+ "integration: requires an explicitly enabled disposable Docker environment",
52
+ ]
53
+
54
+ [tool.ruff]
55
+ target-version = "py312"
56
+ line-length = 120
57
+ include = [
58
+ "src/agent_containers/**/*.py",
59
+ "tests/**/*.py",
60
+ ]
61
+
62
+ [tool.ruff.lint]
63
+ extend-select = [
64
+ "Q",
65
+ "RUF100",
66
+ "RUF018",
67
+ "C90",
68
+ "UP",
69
+ "I",
70
+ "D",
71
+ "TID251",
72
+ ]
73
+ ignore = [
74
+ "D100",
75
+ "D102",
76
+ "D104",
77
+ "D105",
78
+ "D107",
79
+ ]
80
+
81
+ [tool.ruff.lint.mccabe]
82
+ max-complexity = 15
83
+
84
+ [tool.ruff.lint.isort]
85
+ combine-as-imports = true
86
+ known-first-party = ["agent_containers"]
87
+
88
+ [tool.ruff.lint.pydocstyle]
89
+ convention = "google"
90
+
91
+ [tool.ruff.format]
92
+ docstring-code-format = false
93
+
94
+ [tool.ty.environment]
95
+ python-version = "3.12"
96
+
97
+ [tool.ty.rules]
98
+ possibly-unresolved-reference = "warn"
99
+
100
+ [tool.coverage.run]
101
+ source = ["agent_containers"]
102
+ omit = ["tests/*"]
103
+
104
+ [tool.coverage.report]
105
+ fail_under = 100
106
+ skip_covered = true
107
+ show_missing = true
108
+ precision = 2
@@ -0,0 +1,86 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.12.10,<0.13"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "agent-containers"
7
+ version = "0.1.0.dev0"
8
+ description = "Deterministic onboarding and maintenance for hardened coding-agent containers"
9
+ license = "MIT"
10
+ license-files = ["LICEN[CS]E*"]
11
+ readme = "README.md"
12
+ requires-python = ">=3.12"
13
+ dependencies = [
14
+ "pydantic>=2.11,<3",
15
+ "tomlkit>=0.13,<1",
16
+ "typer>=0.16,<1",
17
+ ]
18
+
19
+ classifiers = [
20
+ "Development Status :: 2 - Pre-Alpha",
21
+ "Intended Audience :: Developers",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/anirbanbasu/agent-containers"
28
+ Issues = "https://github.com/anirbanbasu/agent-containers/issues"
29
+ Download = "https://pypi.org/project/agent-containers/"
30
+
31
+ [project.scripts]
32
+ agent-containers = "agent_containers.cli:app"
33
+
34
+ [dependency-groups]
35
+ dev = [
36
+ "ty>=0.0.1,<1",
37
+ "ruff>=0.11,<1",
38
+ ]
39
+ test = [
40
+ "pytest>=8.4.1,<10",
41
+ "coverage>=7.9.1,<8",
42
+ ]
43
+
44
+ [tool.pytest.ini_options]
45
+ testpaths = ["tests"]
46
+ addopts = ["--import-mode=importlib", "--strict-markers"]
47
+ markers = [
48
+ "packaging: builds and installs distributions without Docker",
49
+ "integration: requires an explicitly enabled disposable Docker environment",
50
+ ]
51
+
52
+ [tool.ruff]
53
+ target-version = "py312"
54
+ line-length = 120
55
+ include = ["src/agent_containers/**/*.py", "tests/**/*.py"]
56
+
57
+ [tool.ruff.lint]
58
+ extend-select = ["Q", "RUF100", "RUF018", "C90", "UP", "I", "D", "TID251"]
59
+ mccabe = { max-complexity = 15 }
60
+ ignore = ["D100", "D102", "D104", "D105", "D107"]
61
+
62
+ [tool.ruff.lint.isort]
63
+ combine-as-imports = true
64
+ known-first-party = ["agent_containers"]
65
+
66
+ [tool.ruff.lint.pydocstyle]
67
+ convention = "google"
68
+
69
+ [tool.ruff.format]
70
+ docstring-code-format = false
71
+
72
+ [tool.ty.environment]
73
+ python-version = "3.12"
74
+
75
+ [tool.ty.rules]
76
+ possibly-unresolved-reference = "warn"
77
+
78
+ [tool.coverage.run]
79
+ source = ["agent_containers"]
80
+ omit = ["tests/*"]
81
+
82
+ [tool.coverage.report]
83
+ fail_under = 100
84
+ skip_covered = true
85
+ show_missing = true
86
+ precision = 2
@@ -0,0 +1 @@
1
+ """Deterministic onboarding and maintenance for hardened agent containers."""
@@ -0,0 +1,6 @@
1
+ """Support python -m agent_containers."""
2
+
3
+ from agent_containers.cli import app
4
+
5
+ if __name__ == "__main__":
6
+ app()