Skip to content

Commit 5699de0

Browse files
fix(container): APP_HOST defaults to 127.0.0.1 (HCG tier-2 E1 prereq) (#132)
## Summary Tightens three sites that feed the Zig adapter binary's `--host` flag in production deployments, materialising the ADR-0004 §1 invariant that BoJ's back-side bind is not externally routable when fronted by `http-capability-gateway` (HCG tier-2). This is **action item #7** from the [Phase E consumer-side audit](hyperpolymath/standards#100 (comment)). The scope expanded during implementation from one site (the audit named `stapeln.toml`) to three sites — `entrypoint.sh` and `compose.prod.yaml` had the same `[::]` default that the audit missed. All three sites feed into the same Zig-adapter `--host` flag, so they need to flip together for the change to actually take effect at runtime. Companion PR to **#130** (Cowboy bind tightening in the Elixir path) and **#131** (k8s Service ClusterIP). Together the three give defence in depth: Elixir Cowboy binds loopback **AND** Zig adapter binds loopback **AND** k8s Service is internal-only. `Refs hyperpolymath/standards#100` (NOT Closes — joint-close is owner-only). `Refs hyperpolymath/standards#91`. ## What's in | File | Change | |---|---| | `stapeln.toml` `[targets.production]` | `APP_HOST = "[::]"` → `APP_HOST = "127.0.0.1"` + comment block. | | `container/entrypoint.sh` line 40 (log) + line 140 (`exec` invocation) | `${APP_HOST:-[::]}` → `${APP_HOST:-127.0.0.1}` + comment at exec line. | | `container/compose.prod.yaml` `services.boj-rest.environment` | `APP_HOST: "[::]"` → `APP_HOST: "127.0.0.1"` + comment block. | | `CHANGELOG.md` | New `### Changed` entry under `[Unreleased]`. | ## Override path for legacy/standalone use Deployments without HCG in front: set `APP_HOST=0.0.0.0` (IPv4 all-interfaces) or `APP_HOST=::` (IPv6 all-interfaces) in your deployment config. The in-repo defaults remain loopback. ## Audit-residue follow-ups deliberately NOT in this PR - `container/Containerfile` line 125: `ENV PHX_HOST=0.0.0.0` is **vestigial**. Nothing in the codebase reads `PHX_HOST` (verified by `grep -rn "PHX_HOST\|phx_host" --include="*.ex" ...` returning empty). Leftover from a former Phoenix incarnation. Safe to leave alone; can be removed in a hygiene PR if desired. - Unifying `APP_HOST` (Zig adapter) and `BOJ_BIND_IP` (Elixir Cowboy from #130) into one envelope is broader scope. The divergence exists because they feed different binaries built by different toolchains. If it proves annoying in operation, file a separate issue. ## Why DRAFT Same reason as #131 — this is a behaviour change for anyone running the stapeln-built production container or `compose.prod.yaml` as-is and relying on the default `[::]` for external access. Owner gates merge on confirming no such reliance, or on coordinating with anyone who needs the migration path (HCG-in-front, or explicit `APP_HOST=0.0.0.0` override). ## Test plan - [x] `stapeln.toml` parses as valid TOML (syntax preserved). - [x] `container/entrypoint.sh` runs through `sh -n` without syntax error (no syntax change, just literal substitution). - [x] `container/compose.prod.yaml` parses as valid YAML. - [ ] CI green — governance / hypatia / a2ml / k9 / dogfooding all pass. - [ ] Owner: confirm no live deployment depends on `[::]` default; flip from DRAFT to ready. - [ ] Post-merge manual: a stapeln-built production container, with no env override, refuses connections from non-loopback peers. ## Risk **Low for the codebase, medium for ops.** No Elixir / Zig / Idris2 / cartridge logic touched; CI should not show any regressions. Ops risk: anyone whose runbook assumes the container exposes BoJ on all interfaces by default will need to set `APP_HOST=0.0.0.0` explicitly. Reasonable default for the Phase E posture; documented override path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fb15cd2 commit 5699de0

4 files changed

Lines changed: 33 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ All notable changes to Bundle of Joy Server are documented here.
5656
[`hyperpolymath/standards#100`](https://github.com/hyperpolymath/standards/issues/100),
5757
[`#91`](https://github.com/hyperpolymath/standards/issues/91).
5858

59+
- **Container `APP_HOST` default is now `127.0.0.1`** (was: `"[::]"`
60+
IPv6 all-interfaces). Tightens three sites that feed the Zig adapter
61+
binary's `--host` flag: `stapeln.toml [targets.production]`,
62+
`container/entrypoint.sh`, and `container/compose.prod.yaml`. Same
63+
Phase E posture as the Cowboy bind change in the Elixir path: BoJ
64+
binds loopback by default when fronted by `http-capability-gateway`
65+
(HCG tier-2). Legacy/standalone deployments without HCG in front
66+
should override `APP_HOST=0.0.0.0` (IPv4 all-interfaces) or
67+
`APP_HOST=::` (IPv6 all-interfaces) in their deployment config.
68+
Phase E rollout-runbook §1.4 prereq #7. Refs
69+
[`hyperpolymath/standards#100`](https://github.com/hyperpolymath/standards/issues/100),
70+
[`#91`](https://github.com/hyperpolymath/standards/issues/91).
71+
5972
### Added
6073

6174
- **ADR-0014 — cross-cartridge composition safety (RFC)** — frames the

container/compose.prod.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,12 @@ services:
4040
volumes:
4141
- boj-node-data:/data:Z
4242
environment:
43-
# Server binding
44-
APP_HOST: "[::]"
43+
# Server binding — loopback by default per ADR-0004 §1 (BoJ is
44+
# fronted by http-capability-gateway tier-2 and not externally
45+
# routable). Override APP_HOST=0.0.0.0 or APP_HOST=:: for
46+
# legacy/standalone deployments without HCG in front.
47+
# See docs/integration/hcg-tier2-rollout-runbook.md §1.4 prereq #7.
48+
APP_HOST: "127.0.0.1"
4549
APP_PORT: "7700"
4650
APP_DATA_DIR: "/data"
4751
APP_LOG_FORMAT: "json"

container/entrypoint.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ done
3737
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${CART_LIBS}"
3838

3939
echo "Starting boj-server..."
40-
echo " Host: ${APP_HOST:-[::]}"
40+
echo " Host: ${APP_HOST:-127.0.0.1}"
4141
echo " Port: ${APP_PORT:-7700}"
4242
echo " Data: ${APP_DATA_DIR:-/data}"
4343
echo " Log: ${APP_LOG_FORMAT:-json}"
@@ -137,4 +137,8 @@ bootstrap_federation &
137137
# Replace the entrypoint shell with the application process so that
138138
# signals are delivered directly and PID 1 is the application.
139139

140-
exec /app/boj-server serve --host "${APP_HOST:-[::]}" --port "${REST_PORT}"
140+
# Default to 127.0.0.1 (loopback) per ADR-0004 §1 — BoJ is fronted by
141+
# http-capability-gateway (HCG tier-2) and is not externally routable
142+
# in canonical deployments. Override APP_HOST for legacy/standalone use.
143+
# See docs/integration/hcg-tier2-rollout-runbook.md §1.4 prereq #7.
144+
exec /app/boj-server serve --host "${APP_HOST:-127.0.0.1}" --port "${REST_PORT}"

stapeln.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,14 @@ env = { LOG_LEVEL = "debug", BOJ_DEV_MODE = "true" }
9797

9898
[targets.production]
9999
layers = ["runtime"]
100-
env = { LOG_LEVEL = "info", APP_HOST = "[::]", APP_PORT = "7700" }
100+
# APP_HOST = "127.0.0.1" (was "[::]") — code-enforces the ADR-0004 §1
101+
# invariant that BoJ's back-side bind is not externally routable in
102+
# deployments fronted by http-capability-gateway (HCG tier-2). See
103+
# docs/integration/hcg-tier2-rollout-runbook.md §1.4 prereq #7.
104+
# Legacy/standalone deployments without HCG in front should override
105+
# APP_HOST=0.0.0.0 (IPv4 all-interfaces) or APP_HOST=:: (IPv6
106+
# all-interfaces) in their deployment configuration.
107+
env = { LOG_LEVEL = "info", APP_HOST = "127.0.0.1", APP_PORT = "7700" }
101108

102109
[targets.test]
103110
layers = ["base", "zig-toolchain", "ffi-build", "adapter-build"]

0 commit comments

Comments
 (0)