fix(docker): ship Bedrock auth and current registry (#2982)
## Description Fixes #1551 and #1692. Every published Headroom Docker image now installs the existing `bedrock` extra, so `--backend bedrock` can authenticate with temporary STS, SSO, and credential-process credentials instead of failing because `botocore` is absent. Public Docker instructions now consistently use `ghcr.io/headroomlabs-ai/headroom`. Several still pointed at the old personal package, which is frozen at 0.27.0 and caused users to report that no latest image existed. ## Type of Change - [x] Bug fix - [ ] New feature - [ ] Breaking change - [x] Documentation update - [x] Build / CI ## Changes Made - Add `bedrock` to the standalone Dockerfile default extras. - Add `bedrock` to all nine root/code/slim/nonroot bake targets. - Replace obsolete personal GHCR references in README, llms.txt, Compose guidance, testing guidance, and wiki docs. - Add release contract tests for Bedrock dependencies and the current organization registry. ## Testing - [x] Focused Docker release and Bedrock preflight tests pass. - [x] Full updater suites pass: 69 tests. - [x] `uv run ruff check tests/test_release_workflows.py` - [x] `docker buildx bake --print` - [x] `git diff --check` ## Real Behavior Proof Before this change, every published bake target installed only `proxy` or `proxy,code`, so `AWS_SESSION_TOKEN` selected an unavailable botocore path. Public copy-paste commands also referenced `ghcr.io/chopratejas/headroom`, which the existing migration code and changelog identify as frozen at 0.27.0. After this change, all nine parsed bake targets install `bedrock`; the regression resolves that package extra and confirms `boto3` plus `botocore`. Every public Docker instruction covered by the contract names `ghcr.io/headroomlabs-ai/headroom`. ## Runtime Rollout Safety This changes image contents and documentation only; proxy routing and non-Docker installs are unchanged. Static AWS credentials remain unaffected. Existing manifests using the deprecated image continue to be migrated by the established install-state logic. Rollback is a Docker/bake extras and documentation revert. ## Review Readiness - [x] Two related Docker blockers batched in one PR - [x] Regression coverage included - [x] No unrelated lockfile changes - [x] Ready for review
This commit is contained in:
+4
-1
@@ -47,7 +47,10 @@ COPY Cargo.toml Cargo.lock rust-toolchain.toml ./
|
||||
COPY crates/ crates/
|
||||
COPY headroom/ headroom/
|
||||
|
||||
ARG HEADROOM_EXTRAS=proxy,code
|
||||
# The standalone Dockerfile must support every backend advertised by
|
||||
# `headroom proxy --backend`, including Bedrock temporary/SSO credentials.
|
||||
# Those credentials require botocore (GH #1551), supplied by [bedrock].
|
||||
ARG HEADROOM_EXTRAS=proxy,code,bedrock
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
|
||||
@@ -415,7 +415,7 @@ Everything in this repo stays open source (Apache 2.0). The managed offering is
|
||||
uv tool install --python 3.13 "headroom-ai[all]" # CLI, isolated app env
|
||||
pip install "headroom-ai[all]" # Python, everything — includes the `headroom` CLI
|
||||
npm install headroom-ai # TypeScript SDK (library only — no `headroom` CLI)
|
||||
docker pull ghcr.io/chopratejas/headroom:latest
|
||||
docker pull ghcr.io/headroomlabs-ai/headroom:latest
|
||||
```
|
||||
|
||||
Granular extras: `[proxy]`, `[mcp]`, `[ml]` (Kompress-v2-base), `[code]`, `[memory]`, `[vector]` (optional HNSW backend — needs a C++ toolchain, not in `[all]`), `[relevance]`, `[image]`, `[agno]`, `[langchain]`, `[evals]`, `[pytorch-mps]` (Apple-GPU memory-embedder offload — set `HEADROOM_EMBEDDER_RUNTIME=pytorch_mps`). Requires **Python 3.10+**.
|
||||
|
||||
@@ -107,7 +107,7 @@ There is **no native Windows wheel yet**, so pick one:
|
||||
|
||||
**A. Mechanism test (easiest — Docker Desktop or WSL2):**
|
||||
```powershell
|
||||
$env:HEADROOM_DOCKER_IMAGE = "ghcr.io/chopratejas/headroom:<branch-tag>" # ask the maintainer for the tag
|
||||
$env:HEADROOM_DOCKER_IMAGE = "ghcr.io/headroomlabs-ai/headroom:<branch-tag>" # ask the maintainer for the tag
|
||||
# run the Docker-native installer (scripts/install.ps1), then:
|
||||
$env:GITHUB_COPILOT_TOKEN = "<your-token>"
|
||||
headroom wrap copilot --subscription -- --model gpt-4o -p "Reply with: HEADROOM_OK"
|
||||
|
||||
+9
-9
@@ -10,7 +10,7 @@ target "runtime-default" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy"
|
||||
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||
RUNTIME_USER = "nonroot"
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ target "runtime" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy"
|
||||
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||
RUNTIME_USER = "root"
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ target "runtime-nonroot" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy"
|
||||
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||
RUNTIME_USER = "nonroot"
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ target "runtime-code" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy,code"
|
||||
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||
RUNTIME_USER = "root"
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ target "runtime-code-nonroot" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy,code"
|
||||
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||
RUNTIME_USER = "nonroot"
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ target "runtime-slim" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime-slim"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy"
|
||||
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||
RUNTIME_USER = "root"
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ target "runtime-slim-nonroot" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime-slim"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy"
|
||||
HEADROOM_EXTRAS = "proxy,bedrock"
|
||||
RUNTIME_USER = "nonroot"
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ target "runtime-code-slim" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime-slim"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy,code"
|
||||
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||
RUNTIME_USER = "root"
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ target "runtime-code-slim-nonroot" {
|
||||
inherits = ["_common", "docker-metadata-action"]
|
||||
target = "runtime-slim"
|
||||
args = {
|
||||
HEADROOM_EXTRAS = "proxy,code"
|
||||
HEADROOM_EXTRAS = "proxy,code,bedrock"
|
||||
RUNTIME_USER = "nonroot"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
# 3. point your LLM client at http://localhost:8787 (proxy)
|
||||
#
|
||||
# Just want the proxy without the memory features? You can run the proxy image
|
||||
# on its own (`docker run -p 8787:8787 ghcr.io/chopratejas/headroom`); the two
|
||||
# on its own (`docker run -p 8787:8787 ghcr.io/headroomlabs-ai/headroom`); the two
|
||||
# database services below are only required for the memory/relevance features.
|
||||
#
|
||||
# Ports exposed on the host:
|
||||
|
||||
@@ -19,7 +19,7 @@ The canonical, always-current documentation index lives at the docs site below.
|
||||
|
||||
- Python: `pip install headroom-ai` (add `[all]` for every optional extra)
|
||||
- TypeScript / Node: `npm install headroom-ai` (or `pnpm add headroom-ai`, `bun add headroom-ai`)
|
||||
- Docker: `docker run -p 8787:8787 ghcr.io/chopratejas/headroom:latest`
|
||||
- Docker: `docker run -p 8787:8787 ghcr.io/headroomlabs-ai/headroom:latest`
|
||||
- Run the proxy: `headroom proxy --port 8787` then point any client at `http://127.0.0.1:8787`
|
||||
- Wrap an agent in one command: `headroom wrap claude` (also: `codex`, `copilot`, `cursor`, `aider`, `opencode`, `cline`, `continue`, `goose`, `openhands`, `openclaw`, `vibe`, `omp`)
|
||||
|
||||
|
||||
@@ -6,10 +6,62 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from packaging.requirements import Requirement
|
||||
|
||||
try:
|
||||
import tomllib
|
||||
except ModuleNotFoundError: # pragma: no cover - Python 3.10 fallback
|
||||
import tomli as tomllib # type: ignore[no-redef]
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def test_every_published_docker_variant_includes_bedrock_auth_dependencies() -> None:
|
||||
"""Every image that advertises ``--backend bedrock`` must ship botocore.
|
||||
|
||||
Temporary AWS credentials take LiteLLM's botocore-backed authentication
|
||||
path. The default images previously installed only ``proxy``/``code``, so
|
||||
the documented Docker Bedrock command failed at runtime with
|
||||
``No module named 'botocore'`` (#1551). Keep the standalone Dockerfile and
|
||||
every bake target on the existing ``bedrock`` package extra.
|
||||
"""
|
||||
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
|
||||
assert "ARG HEADROOM_EXTRAS=proxy,code,bedrock" in dockerfile
|
||||
|
||||
bake = (ROOT / "docker-bake.hcl").read_text(encoding="utf-8")
|
||||
extras_lines = [
|
||||
line.strip() for line in bake.splitlines() if line.strip().startswith("HEADROOM_EXTRAS =")
|
||||
]
|
||||
assert len(extras_lines) == 9
|
||||
assert all("bedrock" in line for line in extras_lines), extras_lines
|
||||
|
||||
project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
||||
bedrock_names = {
|
||||
Requirement(requirement).name
|
||||
for requirement in project["project"]["optional-dependencies"]["bedrock"]
|
||||
}
|
||||
assert {"boto3", "botocore"} <= bedrock_names
|
||||
|
||||
|
||||
def test_public_docker_instructions_use_the_current_organization_package() -> None:
|
||||
"""Do not send users back to the personal GHCR package frozen at 0.27.0."""
|
||||
public_docs = (
|
||||
"README.md",
|
||||
"llms.txt",
|
||||
"docker-compose.yml",
|
||||
"TESTING-copilot-subscription.md",
|
||||
"wiki/cli.md",
|
||||
"wiki/docker-install.md",
|
||||
)
|
||||
deprecated = "ghcr.io/chopratejas/headroom"
|
||||
current = "ghcr.io/headroomlabs-ai/headroom"
|
||||
|
||||
for relative_path in public_docs:
|
||||
content = (ROOT / relative_path).read_text(encoding="utf-8")
|
||||
assert deprecated not in content, relative_path
|
||||
assert current in content, relative_path
|
||||
|
||||
|
||||
def test_docker_workflow_normalizes_repository_name_for_signing() -> None:
|
||||
content = (ROOT / ".github" / "workflows" / "docker.yml").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
+2
-2
@@ -637,7 +637,7 @@ Options:
|
||||
(already the default).
|
||||
--image TEXT Docker image to use when runtime=docker or
|
||||
preset=persistent-docker. [default:
|
||||
ghcr.io/chopratejas/headroom:latest]
|
||||
ghcr.io/headroomlabs-ai/headroom:latest]
|
||||
-?, --help Show this message and exit.
|
||||
```
|
||||
|
||||
@@ -665,7 +665,7 @@ headroom install apply --preset persistent-docker --scope user
|
||||
| `--memory` | off | Enable persistent memory in the managed runtime |
|
||||
| `--telemetry` | off | Opt in to anonymous telemetry (off by default) |
|
||||
| `--no-telemetry` | off | Force anonymous telemetry off (already the default) |
|
||||
| `--image` | `ghcr.io/chopratejas/headroom:latest` | Docker image for Docker-backed installs |
|
||||
| `--image` | `ghcr.io/headroomlabs-ai/headroom:latest` | Docker image for Docker-backed installs |
|
||||
|
||||
`apply` stores a manifest under
|
||||
`${HEADROOM_WORKSPACE_DIR}/deploy/<profile>/manifest.json` (default
|
||||
|
||||
@@ -27,7 +27,7 @@ irm https://raw.githubusercontent.com/chopratejas/headroom/main/scripts/install.
|
||||
## What the installer does
|
||||
|
||||
1. Verifies Docker is installed and available.
|
||||
2. Pulls `ghcr.io/chopratejas/headroom:latest` by default, or reuses / pulls `HEADROOM_DOCKER_IMAGE` when you set a custom image override.
|
||||
2. Pulls `ghcr.io/headroomlabs-ai/headroom:latest` by default, or reuses / pulls `HEADROOM_DOCKER_IMAGE` when you set a custom image override.
|
||||
3. Installs a `headroom` wrapper into `~/.local/bin` or `~/bin`.
|
||||
4. Updates shell startup files so the wrapper directory is on `PATH`.
|
||||
|
||||
@@ -41,7 +41,7 @@ The wrapper keeps Headroom inside Docker and mounts host state back into the con
|
||||
|
||||
Port `8787` stays the default, so `http://localhost:8787` works the same way as a native install.
|
||||
|
||||
Published releases also push versioned GHCR tags such as `ghcr.io/chopratejas/headroom:0.5.26`, and those images are built with the same synced package version used for the matching PyPI and npm release.
|
||||
Published releases also push versioned GHCR tags such as `ghcr.io/headroomlabs-ai/headroom:0.35.0`, and those images are built with the same synced package version used for the matching PyPI and npm release.
|
||||
|
||||
## How the wrapper behaves
|
||||
|
||||
@@ -63,7 +63,7 @@ docker run --rm -it \
|
||||
-p 8787:8787 \
|
||||
-v "$PWD:/workspace" \
|
||||
-w /workspace \
|
||||
ghcr.io/chopratejas/headroom:latest \
|
||||
ghcr.io/headroomlabs-ai/headroom:latest \
|
||||
headroom proxy --host 0.0.0.0 --port 8787
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user