Skip to content

ADR-0001: Environment as policy

  • Status: accepted
  • Date: 2026-09-30

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.

  • Enforcement strength and drift resistance.
  • Rollback speed.
  • Ability to constrain egress to the gateway.
  • Coverage of laptops, which cannot be ignored.
  1. Laptop agent and MDM only.
  2. Environment definition first (Dev Container Feature, Coder module, Codespaces prebuilds), with halod and MDM as secondary sinks.
  3. Rely on vendor server-managed settings.

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.

  • 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 halod inside the container and by version-range pins where the CLI supports them).
  • Bad: we maintain several delivery sinks.
  • 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).

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)