The edge node's local dashboard is still using its first-boot default username and password.
The node containers run with network_mode: host, and the op25 container is privileged: true with /dev mounted. Anything reachable through that dashboard is effectively host-level access on the node, so a default credential is not a minor issue there.
Needs real local auth on the node dashboard plus a first-boot flow that forces a credential change, rather than shipping a known default and relying on nobody looking.
The edge node's local dashboard is still using its first-boot default username and password.
The node containers run with `network_mode: host`, and the op25 container is `privileged: true` with `/dev` mounted. Anything reachable through that dashboard is effectively host-level access on the node, so a default credential is not a minor issue there.
Needs real local auth on the node dashboard plus a first-boot flow that forces a credential change, rather than shipping a known default and relying on nobody looking.
Re-scoping: the code side is done. What remains is operational.
drb-edge-node/app/internal/auth.py implements exactly what this issue asked for — username/password from DASHBOARD_USERNAME/DASHBOARD_PASSWORD, scrypt-hashed with a salt persisted by credentials.py, compared with hmac.compare_digest, and two accepted auth paths (a signed session cookie from POST /login, or HTTP Basic for machine callers). warn_if_default_password() is called from the lifespan startup in main.py, so a node still on the default logs a warning every boot.
So the remaining work is not code: node-002's .env in the field still needs DASHBOARD_USERNAME/DASHBOARD_PASSWORD set to something other than admin / CHANGE-ME-drb-default. Until that happens the node is still open to anyone on its LAN, and since the containers run network_mode: host with op25 privileged: true and /dev mounted, that is host-level exposure.
Two known gaps that could reasonably fold into this issue rather than get their own:
The startup warning only reaches the container log. Nobody reads container logs continuously — the same failure mode as server-26#14. A banner on the dashboard itself would actually be seen.
auth.py's module docstring says the call site is "in main.py" via is_using_default_password(); the real call is warn_if_default_password(). Cosmetic, but it cost a grep.
Also worth noting while in this file: the node dashboard is plain HTTP on :80, so the session cookie and Basic credentials both travel unencrypted on the LAN. auth.py is upfront about this. Auth here stops a passerby, not a sniffer.
Re-scoping: **the code side is done. What remains is operational.**
`drb-edge-node/app/internal/auth.py` implements exactly what this issue asked for — username/password from `DASHBOARD_USERNAME`/`DASHBOARD_PASSWORD`, scrypt-hashed with a salt persisted by `credentials.py`, compared with `hmac.compare_digest`, and two accepted auth paths (a signed session cookie from `POST /login`, or HTTP Basic for machine callers). `warn_if_default_password()` is called from the `lifespan` startup in `main.py`, so a node still on the default logs a warning every boot.
So the remaining work is not code: **node-002's `.env` in the field still needs `DASHBOARD_USERNAME`/`DASHBOARD_PASSWORD` set to something other than `admin` / `CHANGE-ME-drb-default`.** Until that happens the node is still open to anyone on its LAN, and since the containers run `network_mode: host` with op25 `privileged: true` and `/dev` mounted, that is host-level exposure.
Two known gaps that could reasonably fold into this issue rather than get their own:
1. The startup warning only reaches the container log. Nobody reads container logs continuously — the same failure mode as server-26#14. A banner on the dashboard itself would actually be seen.
2. `auth.py`'s module docstring says the call site is "in main.py" via `is_using_default_password()`; the real call is `warn_if_default_password()`. Cosmetic, but it cost a grep.
Also worth noting while in this file: the node dashboard is plain HTTP on :80, so the session cookie and Basic credentials both travel unencrypted on the LAN. `auth.py` is upfront about this. Auth here stops a passerby, not a sniffer.
Ruled by board minutes #62 (2026-08-24): P0, ahead of the Gate A/B backlog. Label board:agenda removed. Owner: CTO.
COO (#58) and CTO (#60) both refused to defer this and CISO (#61) named the fix. A dashboard on first-boot default credentials, on a node running network_mode: host beside a privileged op25 container with /dev mounted, is host-level exposure - on hardware already in friends-and-family hands today. It is independent of Gate A and Gate B and does not wait for either.
Ruling: force credential rotation on first boot. No default that survives the first successful login.
This becomes the next single engineering issue the unattended runner picks up.
One explicit instruction: check first whether it is fixable over the existing MQTT/WireGuard remote access. If it genuinely needs hands on the device, say so in a comment and log a physical-visit date - it does not get silently reclassified as a normal code fix and left to age.
Refs server-26#62.
**Ruled by board minutes #62 (2026-08-24): P0, ahead of the Gate A/B backlog. Label `board:agenda` removed. Owner: CTO.**
COO (#58) and CTO (#60) both refused to defer this and CISO (#61) named the fix. A dashboard on first-boot default credentials, on a node running `network_mode: host` beside a privileged `op25` container with `/dev` mounted, is host-level exposure - on hardware already in friends-and-family hands today. It is independent of Gate A and Gate B and does not wait for either.
**Ruling: force credential rotation on first boot.** No default that survives the first successful login.
This becomes the next single engineering issue the unattended runner picks up.
One explicit instruction: **check first whether it is fixable over the existing MQTT/WireGuard remote access.** If it genuinely needs hands on the device, say so in a comment and log a physical-visit date - it does not get silently reclassified as a normal code fix and left to age.
Refs server-26#62.
Board ruling — server-26 FINAL MINUTES #97, 2026-09-01. Escalated to the exclusive next engineering slot.
Minutes #62 ruled this P0 on 2026-08-24 and said it becomes the next single engineering issue the unattended runner picks up. Seven days later Client/drb-edge-node/app/internal/auth.py and credentials.py were last touched before that ruling and no forced-rotation flow ships. It has been silently outranked every night by non-P0 work — including by server-26#64, which could never have consumed an engineering slot at all because its remaining work is owner-gated.
Ruling (#97 D6): this takes the next unattended engineering slot, exclusively. No other engineering item preempts it until it lands. Owner: CTO.
Three things belong to that slot:
The forced credential change on first successful login — no default that survives the first successful login, per minutes #62.
The minutes #62 instruction that was never carried out: check first whether node-002 is fixable over the existing MQTT/WireGuard remote access. If it genuinely needs hands on the device, say so in a comment here and log a physical-visit date. There is no record of this check having been done, which is exactly the silent reclassification #62 forbade.
The dashboard-banner gap from the 2026-08-20 re-scope comment — a boot warning that only reaches a container log nobody reads is not a control.
Separately, a stopgap that does not wait for the code: setting DASHBOARD_USERNAME / DASHBOARD_PASSWORD on node-002 off the shipped defaults is task 3 of a batched ~30-minute owner credential session on 2026-09-02 (#97 D4). That closes today's live exposure; the code fix closes it for every future node.
**Board ruling — server-26 FINAL MINUTES #97, 2026-09-01. Escalated to the exclusive next engineering slot.**
Minutes #62 ruled this P0 on 2026-08-24 and said it *becomes the next single engineering issue the unattended runner picks up*. Seven days later `Client/drb-edge-node/app/internal/auth.py` and `credentials.py` were last touched before that ruling and no forced-rotation flow ships. It has been silently outranked every night by non-P0 work — including by server-26#64, which could never have consumed an engineering slot at all because its remaining work is owner-gated.
**Ruling (#97 D6): this takes the next unattended engineering slot, exclusively. No other engineering item preempts it until it lands. Owner: CTO.**
Three things belong to that slot:
1. The forced credential change on first successful login — *no default that survives the first successful login*, per minutes #62.
2. **The minutes #62 instruction that was never carried out:** check first whether node-002 is fixable over the existing MQTT/WireGuard remote access. If it genuinely needs hands on the device, say so in a comment here and log a physical-visit date. There is no record of this check having been done, which is exactly the silent reclassification #62 forbade.
3. The dashboard-banner gap from the 2026-08-20 re-scope comment — a boot warning that only reaches a container log nobody reads is not a control.
**Separately, a stopgap that does not wait for the code:** setting `DASHBOARD_USERNAME` / `DASHBOARD_PASSWORD` on node-002 off the shipped defaults is task 3 of a batched ~30-minute owner credential session on **2026-09-02** (#97 D4). That closes today's live exposure; the code fix closes it for every future node.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The edge node's local dashboard is still using its first-boot default username and password.
The node containers run with
network_mode: host, and the op25 container isprivileged: truewith/devmounted. Anything reachable through that dashboard is effectively host-level access on the node, so a default credential is not a minor issue there.Needs real local auth on the node dashboard plus a first-boot flow that forces a credential change, rather than shipping a known default and relying on nobody looking.
Re-scoping: the code side is done. What remains is operational.
drb-edge-node/app/internal/auth.pyimplements exactly what this issue asked for — username/password fromDASHBOARD_USERNAME/DASHBOARD_PASSWORD, scrypt-hashed with a salt persisted bycredentials.py, compared withhmac.compare_digest, and two accepted auth paths (a signed session cookie fromPOST /login, or HTTP Basic for machine callers).warn_if_default_password()is called from thelifespanstartup inmain.py, so a node still on the default logs a warning every boot.So the remaining work is not code: node-002's
.envin the field still needsDASHBOARD_USERNAME/DASHBOARD_PASSWORDset to something other thanadmin/CHANGE-ME-drb-default. Until that happens the node is still open to anyone on its LAN, and since the containers runnetwork_mode: hostwith op25privileged: trueand/devmounted, that is host-level exposure.Two known gaps that could reasonably fold into this issue rather than get their own:
auth.py's module docstring says the call site is "in main.py" viais_using_default_password(); the real call iswarn_if_default_password(). Cosmetic, but it cost a grep.Also worth noting while in this file: the node dashboard is plain HTTP on :80, so the session cookie and Basic credentials both travel unencrypted on the LAN.
auth.pyis upfront about this. Auth here stops a passerby, not a sniffer.Ruled by board minutes #62 (2026-08-24): P0, ahead of the Gate A/B backlog. Label
board:agendaremoved. Owner: CTO.COO (#58) and CTO (#60) both refused to defer this and CISO (#61) named the fix. A dashboard on first-boot default credentials, on a node running
network_mode: hostbeside a privilegedop25container with/devmounted, is host-level exposure - on hardware already in friends-and-family hands today. It is independent of Gate A and Gate B and does not wait for either.Ruling: force credential rotation on first boot. No default that survives the first successful login.
This becomes the next single engineering issue the unattended runner picks up.
One explicit instruction: check first whether it is fixable over the existing MQTT/WireGuard remote access. If it genuinely needs hands on the device, say so in a comment and log a physical-visit date - it does not get silently reclassified as a normal code fix and left to age.
Refs server-26#62.
Board ruling — server-26 FINAL MINUTES #97, 2026-09-01. Escalated to the exclusive next engineering slot.
Minutes #62 ruled this P0 on 2026-08-24 and said it becomes the next single engineering issue the unattended runner picks up. Seven days later
Client/drb-edge-node/app/internal/auth.pyandcredentials.pywere last touched before that ruling and no forced-rotation flow ships. It has been silently outranked every night by non-P0 work — including by server-26#64, which could never have consumed an engineering slot at all because its remaining work is owner-gated.Ruling (#97 D6): this takes the next unattended engineering slot, exclusively. No other engineering item preempts it until it lands. Owner: CTO.
Three things belong to that slot:
Separately, a stopgap that does not wait for the code: setting
DASHBOARD_USERNAME/DASHBOARD_PASSWORDon node-002 off the shipped defaults is task 3 of a batched ~30-minute owner credential session on 2026-09-02 (#97 D4). That closes today's live exposure; the code fix closes it for every future node.