Bug description
Visiting the orchestrator root (http://host:3000/) in a browser to open the admin UI (added in 0.0.4) returns:
{"detail":"Missing Authorization header"}
instead of serving the UI. This happens with the official Docker image (ghcr.io/open-webui/terminals:0.0.5) and any image/install built with pip install ., whenever TERMINALS_API_KEY or TERMINALS_OPEN_WEBUI_URL is set.
Steps to reproduce
- Run the official image, e.g.
docker run -p 3000:3000 -e TERMINALS_API_KEY=secret -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/open-webui/terminals:0.0.5
- Open
http://localhost:3000/ in a browser.
Expected: the admin UI loads (TERMINALS_ENABLE_UI defaults to true).
Actual: 401 {"detail":"Missing Authorization header"}.
Root cause
The built frontend never makes it into the installed Python package:
-
terminals/frontend/build/ is gitignored (root .gitignore has build/, and terminals/frontend/.gitignore has build).
-
Hatchling honors .gitignore during wheel file selection and excludes VCS-ignored files even when they match an include pattern. The "terminals/frontend/build/**/*" entry under [tool.hatch.build.targets.wheel].include in pyproject.toml is therefore a no-op. Hatchling's documented override for shipping gitignored build outputs is the separate artifacts option, which is not used.
-
So the wheel built by pip install . in the Dockerfile ships the frontend source (src/, package.json, …) but not frontend/build/. Verifiable in a running container:
$ docker exec <container> pip show -f terminals | grep frontend
terminals/frontend/.gitignore
terminals/frontend/package.json
terminals/frontend/src/routes/+page.svelte
... # <-- no frontend/build/* entries
-
At runtime the terminals console script imports the package from site-packages, where FRONTEND_BUILD_DIR.exists() (terminals/main.py) is false, so the GET / UI route is never registered.
-
The browser request falls through to the auth-protected catch-all reverse proxy (/{path:path} in terminals/routers/proxy.py), whose verify_api_key dependency rejects it with 401 Missing Authorization header.
Debugging note: the image does contain the built UI at /app/terminals/frontend/build (the Dockerfile COPY --from=frontend target), which is misleading — that copy is dead weight; the app runs from site-packages.
Fix (verified)
Declare the build output as hatchling artifacts in pyproject.toml:
[tool.hatch.build.targets.wheel]
packages = ["terminals"]
include = [
"terminals/**/*.py",
"terminals/alembic.ini",
"terminals/migrations/**/*",
"terminals/frontend/build/**/*",
]
artifacts = [
"terminals/frontend/build/**/*",
]
Rebuilding the wheel with this change makes terminals/frontend/build/** appear in the wheel, and the UI is then served on /.
Workaround for existing deployments
Until a fixed image is published, make Python prefer the complete source tree already inside the image, e.g. in docker-compose:
services:
terminals:
environment:
PYTHONPATH: "/app"
or copy the build into site-packages:
docker exec <container> sh -c 'cp -r /app/terminals/frontend/build "$(pip show terminals | sed -n "s/^Location: //p")/terminals/frontend/"'
docker restart <container>
Affected versions
0.0.4 (UI introduced) and 0.0.5.
Bug description
Visiting the orchestrator root (
http://host:3000/) in a browser to open the admin UI (added in 0.0.4) returns:{"detail":"Missing Authorization header"}instead of serving the UI. This happens with the official Docker image (
ghcr.io/open-webui/terminals:0.0.5) and any image/install built withpip install ., wheneverTERMINALS_API_KEYorTERMINALS_OPEN_WEBUI_URLis set.Steps to reproduce
docker run -p 3000:3000 -e TERMINALS_API_KEY=secret -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/open-webui/terminals:0.0.5http://localhost:3000/in a browser.Expected: the admin UI loads (
TERMINALS_ENABLE_UIdefaults totrue).Actual:
401 {"detail":"Missing Authorization header"}.Root cause
The built frontend never makes it into the installed Python package:
terminals/frontend/build/is gitignored (root.gitignorehasbuild/, andterminals/frontend/.gitignorehasbuild).Hatchling honors
.gitignoreduring wheel file selection and excludes VCS-ignored files even when they match anincludepattern. The"terminals/frontend/build/**/*"entry under[tool.hatch.build.targets.wheel].includeinpyproject.tomlis therefore a no-op. Hatchling's documented override for shipping gitignored build outputs is the separateartifactsoption, which is not used.So the wheel built by
pip install .in the Dockerfile ships the frontend source (src/,package.json, …) but notfrontend/build/. Verifiable in a running container:At runtime the
terminalsconsole script imports the package from site-packages, whereFRONTEND_BUILD_DIR.exists()(terminals/main.py) is false, so theGET /UI route is never registered.The browser request falls through to the auth-protected catch-all reverse proxy (
/{path:path}interminals/routers/proxy.py), whoseverify_api_keydependency rejects it with401 Missing Authorization header.Debugging note: the image does contain the built UI at
/app/terminals/frontend/build(the DockerfileCOPY --from=frontendtarget), which is misleading — that copy is dead weight; the app runs from site-packages.Fix (verified)
Declare the build output as hatchling
artifactsinpyproject.toml:Rebuilding the wheel with this change makes
terminals/frontend/build/**appear in the wheel, and the UI is then served on/.Workaround for existing deployments
Until a fixed image is published, make Python prefer the complete source tree already inside the image, e.g. in docker-compose:
or copy the build into site-packages:
Affected versions
0.0.4 (UI introduced) and 0.0.5.