feat(frank-dragoon): put the endpoint on the app bridge so app containers can name it
An endpoint on podman's rootless default (pasta) has no address another
container can use, measured on 2026-10-19 from inside open-notebook:
- 192.168.0.143:7770 connection refused (pasta forwards the
published port in the host netns; no netavark
DNAT exists for traffic arriving from a
rootless bridge)
- host.containers.internal:7770 200, but the alias resolves to 169.254.1.2 —
link-local, which open-notebook's URL guard
refuses to store
- frank-dragoon-halogen:8731 name unknown: different netns
- --network pasta --network dankclaw_default
"cannot set multiple networks without bridge
network mode" — pasta cannot be added to
So reaching the NPU endpoints from an app container means moving the container
onto dankclaw_default (FRANK_DRAGOON_NETWORK; empty keeps today's shape). This
is not an exposure change: the engine's unauthenticated token port binds the
container's own loopback (/proc/net/tcp: 127.0.0.1:8730 against 0.0.0.0:8731),
so only the API port becomes name-resolvable — the same trust the published
7770 already carries on the LAN. The deployer refuses a network that does not
exist rather than starting a unit that restart-loops on it.
Co-Authored-By: omp <omp@dankclaw>
This commit is contained in:
1 parent
53ef121180
commit
346e1d22f4
4 files changed
+60
No files matched your search
@@ -1348,6 +1348,7 @@ LLM_DRAGOON_NPU_MODELS="${LLM_DRAGOON_NPU_MODELS:-qwen3-embedding-0.6b}"
|
|||||||
LLM_DRAGOON_NPU_DIR="${LLM_DRAGOON_NPU_DIR:-/home/dank/models/halogen-npu}"
|
LLM_DRAGOON_NPU_DIR="${LLM_DRAGOON_NPU_DIR:-/home/dank/models/halogen-npu}"
|
||||||
LLM_DRAGOON_DEV_ACCEL="${LLM_DRAGOON_DEV_ACCEL:-/dev/accel/accel0}"
|
LLM_DRAGOON_DEV_ACCEL="${LLM_DRAGOON_DEV_ACCEL:-/dev/accel/accel0}"
|
||||||
LLM_DRAGOON_XRT_DIR="${LLM_DRAGOON_XRT_DIR:-/usr/lib/x86_64-linux-gnu}"
|
LLM_DRAGOON_XRT_DIR="${LLM_DRAGOON_XRT_DIR:-/usr/lib/x86_64-linux-gnu}"
|
||||||
|
LLM_DRAGOON_NETWORK="${LLM_DRAGOON_NETWORK:-dankclaw_default}"
|
||||||
|
|
||||||
retire_halogen_dragoon_quadlet() {
|
retire_halogen_dragoon_quadlet() {
|
||||||
local trash="$HOME/.AGENTTRASH/20260919-orca-cutover/quadlet"
|
local trash="$HOME/.AGENTTRASH/20260919-orca-cutover/quadlet"
|
||||||
@@ -1426,6 +1427,14 @@ deploy_llm_dragoon() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# The container network has to exist before the run line names it (a typo
|
||||||
|
# there is a restart-looping unit, not a config error anyone reads).
|
||||||
|
if [[ -n "$LLM_DRAGOON_NETWORK" ]] && ! podman network exists "$LLM_DRAGOON_NETWORK"; then
|
||||||
|
echo "ERROR: podman network $LLM_DRAGOON_NETWORK does not exist" >&2
|
||||||
|
echo "frank-dragoon: create it or blank FRANK_DRAGOON_NETWORK (pasta default)" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# --- NPU lane: the same three predicates the serve script checks ---------
|
# --- NPU lane: the same three predicates the serve script checks ---------
|
||||||
if [[ -n "$LLM_DRAGOON_NPU_MODELS" ]]; then
|
if [[ -n "$LLM_DRAGOON_NPU_MODELS" ]]; then
|
||||||
if [[ ! -e "$LLM_DRAGOON_DEV_ACCEL" ]]; then
|
if [[ ! -e "$LLM_DRAGOON_DEV_ACCEL" ]]; then
|
||||||
|
|||||||
@@ -110,3 +110,12 @@ FRANK_DRAGOON_XRT_DIR=/usr/lib/x86_64-linux-gnu
|
|||||||
# sysfs root the fabric-clock preflight reads (same knob the vendor's
|
# sysfs root the fabric-clock preflight reads (same knob the vendor's
|
||||||
# halogen-fabric-clock status reads; the seam points it at a fixture).
|
# halogen-fabric-clock status reads; the seam points it at a fixture).
|
||||||
FRANK_DRAGOON_SYSFS=/sys
|
FRANK_DRAGOON_SYSFS=/sys
|
||||||
|
# Container network. Empty = podman's rootless default (`pasta`): a private
|
||||||
|
# netns that nothing else can join — podman refuses a second network while the
|
||||||
|
# primary is pasta, the published port is refused from dankclaw_default
|
||||||
|
# containers, and host.containers.internal resolves link-local, which
|
||||||
|
# open-notebook's URL guard refuses to store. Named = the app bridge, so
|
||||||
|
# open-notebook can dial http://frank-dragoon-halogen:8731 for the NPU
|
||||||
|
# endpoints. The engine's unauthenticated 8730 stays private either way (it
|
||||||
|
# binds the container's own loopback); only the API port becomes name-resolvable.
|
||||||
|
FRANK_DRAGOON_NETWORK=dankclaw_default
|
||||||
@@ -187,6 +187,24 @@ if [ -n "$NPU_MODELS" ]; then
|
|||||||
-e HALOGEN_NPU_MODELS="$NPU_MODELS")
|
-e HALOGEN_NPU_MODELS="$NPU_MODELS")
|
||||||
echo "frank-dragoon: NPU lane armed: $NPU_MODELS on $DEV_ACCEL (fabric clock held)" >&2
|
echo "frank-dragoon: NPU lane armed: $NPU_MODELS on $DEV_ACCEL (fabric clock held)" >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Container network. Empty = podman's rootless default (`pasta`), which is a
|
||||||
|
# private netns and CANNOT be joined by anything else: podman refuses a second
|
||||||
|
# network while the primary is pasta ("cannot set multiple networks without
|
||||||
|
# bridge network mode"), the endpoint's published port answers 200 on the host
|
||||||
|
# and on the LAN but is REFUSED from a dankclaw_default container (measured
|
||||||
|
# 2026-10-19), and `host.containers.internal:7770` answers 200 yet resolves to
|
||||||
|
# 169.254.1.2 — link-local, which open-notebook's URL guard refuses to store.
|
||||||
|
# So an endpoint on pasta has no address an app container can use; naming the
|
||||||
|
# app bridge here is the only shape that lets open-notebook dial
|
||||||
|
# http://frank-dragoon-halogen:8731 for the NPU endpoints. It does not widen
|
||||||
|
# the surface: the engine's unauthenticated 8730 is bound to the container's
|
||||||
|
# OWN loopback (/proc/net/tcp: 127.0.0.1:8730 vs 0.0.0.0:8731, checked the same
|
||||||
|
# day), so only the API port is reachable by name.
|
||||||
|
NET_ARGS=()
|
||||||
|
if [ -n "${FRANK_DRAGOON_NETWORK:-}" ]; then
|
||||||
|
NET_ARGS=(--network "$FRANK_DRAGOON_NETWORK")
|
||||||
|
fi
|
||||||
# Host-RAM preflight — the engine's pin gate, checked BEFORE podman run.
|
# Host-RAM preflight — the engine's pin gate, checked BEFORE podman run.
|
||||||
# The engine refuses to pin when MemAvailable is below the checkpoint plus
|
# The engine refuses to pin when MemAvailable is below the checkpoint plus
|
||||||
# a 16 GiB floor: pinned pages cannot be swapped, and under the floor the
|
# a 16 GiB floor: pinned pages cannot be swapped, and under the floor the
|
||||||
@@ -261,6 +279,7 @@ podman run --name "$FRANK_DRAGOON_CONTAINER" \
|
|||||||
${HALOGEN_WEIGHTS_LOCK:+-e HALOGEN_WEIGHTS_LOCK="$HALOGEN_WEIGHTS_LOCK"} \
|
${HALOGEN_WEIGHTS_LOCK:+-e HALOGEN_WEIGHTS_LOCK="$HALOGEN_WEIGHTS_LOCK"} \
|
||||||
-v "$FRANK_DRAGOON_MODEL_DIR:/models:ro" \
|
-v "$FRANK_DRAGOON_MODEL_DIR:/models:ro" \
|
||||||
-v "$FRANK_DRAGOON_MODEL_DIR/tokenizer:/tokenizer:ro" \
|
-v "$FRANK_DRAGOON_MODEL_DIR/tokenizer:/tokenizer:ro" \
|
||||||
|
"${NET_ARGS[@]}" \
|
||||||
"${NPU_ARGS[@]}" \
|
"${NPU_ARGS[@]}" \
|
||||||
"$FRANK_DRAGOON_IMAGE" \
|
"$FRANK_DRAGOON_IMAGE" \
|
||||||
> "$LOG" 2>&1
|
> "$LOG" 2>&1
|
||||||
|
|||||||
@@ -357,6 +357,8 @@ $(cat "$TEST_TMP/serve.log")"
|
|||||||
npu_models="$(sed -nE 's/^HALOGEN_NPU_MODELS=(.*)$/\1/p' "$ENV_FILE")"
|
npu_models="$(sed -nE 's/^HALOGEN_NPU_MODELS=(.*)$/\1/p' "$ENV_FILE")"
|
||||||
grep -qF "LLM_DRAGOON_NPU_DIR=\"\${LLM_DRAGOON_NPU_DIR:-$npu_dir}\"" "$DEPLOYER"
|
grep -qF "LLM_DRAGOON_NPU_DIR=\"\${LLM_DRAGOON_NPU_DIR:-$npu_dir}\"" "$DEPLOYER"
|
||||||
grep -qF "LLM_DRAGOON_NPU_MODELS=\"\${LLM_DRAGOON_NPU_MODELS:-$npu_models}\"" "$DEPLOYER"
|
grep -qF "LLM_DRAGOON_NPU_MODELS=\"\${LLM_DRAGOON_NPU_MODELS:-$npu_models}\"" "$DEPLOYER"
|
||||||
|
net="$(sed -nE 's/^FRANK_DRAGOON_NETWORK=(.*)$/\1/p' "$ENV_FILE")"
|
||||||
|
grep -qF "LLM_DRAGOON_NETWORK=\"\${LLM_DRAGOON_NETWORK:-$net}\"" "$DEPLOYER"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "the deployer points a missing artifact at the v2abl fetch script" {
|
@test "the deployer points a missing artifact at the v2abl fetch script" {
|
||||||
@@ -583,3 +585,24 @@ $(cat "$TEST_TMP/serve.log")"
|
|||||||
# and curl drops Range across it (the sibling 62 GiB fetch, 2026-10-18).
|
# and curl drops Range across it (the sibling 62 GiB fetch, 2026-10-18).
|
||||||
grep -qE 'url_effective' "$NPU_FETCH"
|
grep -qE 'url_effective' "$NPU_FETCH"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "the container network is podman's default when unnamed and the app bridge when named" {
|
||||||
|
# The app-to-endpoint leg hangs on this: pasta is a private netns nothing
|
||||||
|
# else can join (podman refuses a second network, the published port is
|
||||||
|
# refused from app containers, and host.containers.internal is link-local,
|
||||||
|
# which open-notebook refuses to store), so reaching the NPU endpoints by
|
||||||
|
# name means the container lives on dankclaw_default. Both arms are
|
||||||
|
# observable in the command podman would run.
|
||||||
|
podman_stub
|
||||||
|
good_env
|
||||||
|
run_with_log FRANK_DRAGOON_NETWORK=
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
! grep -q -- '--network' "$TEST_TMP/serve.log"
|
||||||
|
run_with_log FRANK_DRAGOON_NETWORK=dankclaw_default
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == *"--network dankclaw_default"* ]]
|
||||||
|
# The deployer refuses a network that does not exist rather than starting a
|
||||||
|
# unit that restart-loops on it.
|
||||||
|
grep -qE 'podman network exists' "$DEPLOYER"
|
||||||
|
grep -qE '^FRANK_DRAGOON_NETWORK=dankclaw_default$' "$ENV_FILE"
|
||||||
|
}
|
||||||
Reference in new issue
Block a user