ADR-0001: Environment as policy
ADR-0001: Environment as policy
Section titled “ADR-0001: Environment as policy”- Status: accepted
- Date: 2026-09-30
Context and problem statement
Section titled “Context and problem statement”Claude Code’s server-managed settings are not fetched when traffic goes through Bedrock or a custom ANTHROPIC_BASE_URL, which is exactly the gateway topology this project targets. Configuration must therefore arrive as files (managed-settings.json, managed-settings.d/), MDM profiles, or environment variables. Laptop agents and MDM are weak enforcement points: the user is often local admin, MDM is slow, and drift is common. Meanwhile a growing share of AI-assisted work runs in dev containers, Codespaces and Coder workspaces, and unattended agents (Coder Tasks, CI) run only there.
Decision drivers
Section titled “Decision drivers”- Enforcement strength and drift resistance.
- Rollback speed.
- Ability to constrain egress to the gateway.
- Coverage of laptops, which cannot be ignored.
Considered options
Section titled “Considered options”- Laptop agent and MDM only.
- Environment definition first (Dev Container Feature, Coder module, Codespaces prebuilds), with
halodand MDM as secondary sinks. - Rely on vendor server-managed settings.
Decision outcome
Section titled “Decision outcome”Chosen: option 2. The environment definition is the primary delivery path. A ring change is a rebuild from a signed release. halod (laptops) and MDM exports (Jamf, Kandji, Intune) are supported sinks that consume the same release artifact.
Consequences
Section titled “Consequences”- Good: drift is prevented by construction; egress can be firewalled to the gateway; rollback is a rebuild.
- Good: one signed artifact feeds every sink.
- Bad: laptops get weaker guarantees, and long-lived containers can lag until rebuilt (mitigated by
halodinside the container and by version-range pins where the CLI supports them). - Bad: we maintain several delivery sinks.
Rejected options
Section titled “Rejected options”- Option 1: weakest enforcement, slowest rollback.
- Option 3: not available on the Bedrock/custom-gateway path.
Addendum 2026-10-02: exact pins, not version ranges
Section titled “Addendum 2026-10-02: exact pins, not version ranges”The consequence above mentions “version-range pins where the CLI supports them” as a mitigation for long-lived containers. That is superseded. Every ring profile pins each harness to an exact version (policy.Validate refuses ranges, and ADR-0010 needs exact pins to compare a variant with its ring). Claude Code receives the pin as requiredMinimumVersion and requiredMaximumVersion set to the same value. A range would make “which bytes did this client run” (ADR-0002) unanswerable. Lag in long-lived containers is handled by halod instead: the Feature runs halod once --install at start, and a running container stays current with "postStartCommand": "sudo /usr/local/lib/halos/halod once --install" (see the dev containers guide).
Implementation status (2026-10-02)
Section titled “Implementation status (2026-10-02)”| Commitment | Code | Test | Status |
|---|---|---|---|
| Dev Container Feature consumes the signed release | features/halos/install.sh (sha256-pinned halod, inline release key, halod once --install); internal/delivery/devcontainer refuses an unverified release |
devcontainer_test.go TestExportRefusals; test/uat TestCLIs (feature step); scripts/package-check.sh feature (devcontainer up) |
Done |
| Coder module | features/coder/main.tf |
none: Terraform is not executed in CI | Partial (UNVERIFIED, tracked in delivery) |
| Codespaces prebuilds | portal launcher internal/server/portal.go; prebuilds use the Feature |
portal_test.go (launcher URL) |
Partial: the prebuild itself is the org’s GitHub config, not shipped here (UNVERIFIED) |
halod and MDM consume the same release |
cmd/halod/agent.go (cycle); internal/delivery/mdm (ExportJamf also serves Kandji, ExportIntune) |
cmd/halod TestRefusesUnverified; mdm_test.go TestJamf, TestIntune, TestUnverifiedRefused |
Done |
halod inside the container |
Feature install.sh; postStartCommand for running containers |
TestCLIs |
Done |
| Version-range pins | superseded by exact pins (addendum above) | claudecode_test.go TestSemantics |
Superseded |
| Egress firewalled to the gateway | features/halos/init-firewall.sh (default drop, gateway and registry allowed) |
devcontainer_test.go TestExport; scripts/package-check.sh feature |
Done (containers only; laptops are the weaker sink by design) |