The image shipped without [unix_http_server], [rpcinterface:supervisor] or [supervisorctl], so supervisorctl cannot connect:
# docker exec heating-can supervisorctl -c /etc/supervisord.conf status
Error: .ini file does not include supervisorctl section
That means there is no way to query or restart an individual service inside the container — docker logs is the only introspection.
Per-service lifecycle management was the entire argument for running a supervisor instead of one process per container. The first revision didn't deliver it.
It cost something concrete during the 2026-08-10 cutover: with two open3e instances fighting over the same MQTT session, supervisorctl status would have shown each service's state and restart count directly. Instead I inferred health from docker logs and reported success while the broker was kicking them off in a loop.
Change
Unix socket only, inside the container. Nothing outside needs it, and this container runs with host networking, so a TCP listener would be exposed on the host.
The build-time self-test now asserts the section exists and supervisorctl runs, so it can't regress silently the way it shipped.
Note
The MQTT reconnect loop this would have surfaced is already fixed — by #477, not by this PR. The cause was the migration failing to stop the native units, leaving a second open3e on the bus. This is about being able to see such a thing next time.
Verification
Build-time only: the self-test runs inside the image during the kaniko build, so a broken config fails the build rather than being published. Not applied to edge-1 — that needs a rebuild and a docker compose pull && up.
The image shipped without `[unix_http_server]`, `[rpcinterface:supervisor]` or `[supervisorctl]`, so `supervisorctl` cannot connect:
```
# docker exec heating-can supervisorctl -c /etc/supervisord.conf status
Error: .ini file does not include supervisorctl section
```
That means there is no way to query or restart an individual service inside the container — `docker logs` is the only introspection.
**Per-service lifecycle management was the entire argument for running a supervisor** instead of one process per container. The first revision didn't deliver it.
It cost something concrete during the 2026-08-10 cutover: with two open3e instances fighting over the same MQTT session, `supervisorctl status` would have shown each service's state and restart count directly. Instead I inferred health from `docker logs` and reported success while the broker was kicking them off in a loop.
## Change
Unix socket only, inside the container. Nothing outside needs it, and this container runs with host networking, so a TCP listener would be exposed on the host.
The build-time self-test now asserts the section exists and `supervisorctl` runs, so it can't regress silently the way it shipped.
## Note
The MQTT reconnect loop this would have surfaced is **already fixed** — by #477, not by this PR. The cause was the migration failing to stop the native units, leaving a second open3e on the bus. This is about being able to see such a thing next time.
## Verification
Build-time only: the self-test runs inside the image during the kaniko build, so a broken config fails the build rather than being published. Not applied to edge-1 — that needs a rebuild and a `docker compose pull && up`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The image shipped without [unix_http_server], [rpcinterface:supervisor] or
[supervisorctl], so supervisorctl could not connect:
Error: .ini file does not include supervisorctl section
That means no way to query or restart an individual service inside the
container -- docker logs was the only introspection. Per-service lifecycle
management was the whole argument for running a supervisor instead of one
process per container, and the first revision did not actually deliver it.
Unix socket only, inside the container. Nothing outside needs it, and this
container runs with host networking, so a TCP listener would be exposed on
the host.
The build-time self-test now asserts the section exists and supervisorctl
runs, so this cannot regress silently the way it shipped.
NOT fixed here, because my diagnosis was wrong: the repeated
"mqtt broker disconnected. reason_code = Unspecified error" is not a client
ID collision. E3onCAN has no -mcid option at all -- verified against the
pinned source -- and generates its own ID; the native broker log showed
E3onCANclient.py-e380-7f847281 alongside open3e-edge, so the two never
collided. I nearly "fixed" it by passing a flag that does not exist.
Telemetry is flowing (each reconnect succeeds), so this is churn rather than
an outage, but the cause is still unidentified. The next step is the broker's
own log on edge-1 -- mosquitto records the disconnect reason, which the
client side reports only as "Unspecified error".
Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🤖Automated review — model claude-haiku-4-5-20251001 (complexity: trivial), commit e3dea169b00d037c612f44f0c170c82cc8861bd0.
Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.
Summary
This PR adds Unix domain socket support to supervisord in the heating-can container, enabling supervisorctl to query and control individual services. The change adds three supervisord config sections and build-time validation; no logic or runtime behavior changes beyond enabling the new interface.
🟢 Nits
Self-test comprehensiveness: The build-time test checks that supervisorctl --help works, which only verifies the binary exists. It doesn't test that supervisorctl can actually connect to a running supervisord instance (that would require starting supervisord, which isn't necessary at build time). However, full validation happens at runtime when supervisord starts and parses the config—it will fail if the configuration is broken. The current test is appropriate for a build-time gate.
Tests
The PR adds build-time validation that:
Both service binaries exist and are executable (open3e --help, E3onCANcollect.py --help)
supervisord can start (supervisord --help)
The config sections are present (grep -q '^\[supervisorctl\]')
supervisorctl binary is executable (supervisorctl --help)
Runtime validation occurs when the container starts: supervisord will fail to start if the config has syntax errors. The socket will be created in /tmp (writable by the open3e user) with 0700 permissions (owner-only access), and supervisorctl can connect via unix:///tmp/supervisor.sock.
Configuration Details
The three new config sections are syntactically correct:
[unix_http_server] (supervisord.conf:21-23): Creates the socket at /tmp/supervisor.sock with restrictive permissions (0700). /tmp is writable and ephemeral, appropriate for container-scoped runtime state.
[rpcinterface:supervisor] (supervisord.conf:25-26): Registers the RPC factory, a mandatory prerequisite for supervisorctl.
[supervisorctl] (supervisord.conf:28-29): Points to the Unix socket using the triple-slash syntax (unix:///tmp/supervisor.sock), which is correct for supervisord.
No port is exposed on the host (the config uses only Unix socket, not TCP), which is appropriate given the container runs with network_mode: host and should not expose privileged management interfaces.
Recommendation
Looks reasonable — no blocking issues; comments above are optional.
🤖 **Automated review** — model `claude-haiku-4-5-20251001` (complexity: trivial), commit `e3dea169b00d037c612f44f0c170c82cc8861bd0`.
> Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review.
---
## Summary
This PR adds Unix domain socket support to supervisord in the heating-can container, enabling `supervisorctl` to query and control individual services. The change adds three supervisord config sections and build-time validation; no logic or runtime behavior changes beyond enabling the new interface.
## 🟢 Nits
- **Self-test comprehensiveness**: The build-time test checks that `supervisorctl --help` works, which only verifies the binary exists. It doesn't test that `supervisorctl` can actually connect to a running supervisord instance (that would require starting supervisord, which isn't necessary at build time). However, full validation happens at runtime when supervisord starts and parses the config—it will fail if the configuration is broken. The current test is appropriate for a build-time gate.
## Tests
The PR adds build-time validation that:
1. Both service binaries exist and are executable (`open3e --help`, `E3onCANcollect.py --help`)
2. supervisord can start (`supervisord --help`)
3. The config sections are present (`grep -q '^\[supervisorctl\]'`)
4. supervisorctl binary is executable (`supervisorctl --help`)
Runtime validation occurs when the container starts: supervisord will fail to start if the config has syntax errors. The socket will be created in `/tmp` (writable by the `open3e` user) with `0700` permissions (owner-only access), and `supervisorctl` can connect via `unix:///tmp/supervisor.sock`.
## Configuration Details
The three new config sections are syntactically correct:
- **`[unix_http_server]`** (`supervisord.conf:21-23`): Creates the socket at `/tmp/supervisor.sock` with restrictive permissions (`0700`). `/tmp` is writable and ephemeral, appropriate for container-scoped runtime state.
- **`[rpcinterface:supervisor]`** (`supervisord.conf:25-26`): Registers the RPC factory, a mandatory prerequisite for supervisorctl.
- **`[supervisorctl]`** (`supervisord.conf:28-29`): Points to the Unix socket using the triple-slash syntax (`unix:///tmp/supervisor.sock`), which is correct for supervisord.
No port is exposed on the host (the config uses only Unix socket, not TCP), which is appropriate given the container runs with `network_mode: host` and should not expose privileged management interfaces.
## Recommendation
Looks reasonable — no blocking issues; comments above are optional.
<!-- pr-review:sha=e3dea169b00d037c612f44f0c170c82cc8861bd0 -->
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 image shipped without
[unix_http_server],[rpcinterface:supervisor]or[supervisorctl], sosupervisorctlcannot connect:That means there is no way to query or restart an individual service inside the container —
docker logsis the only introspection.Per-service lifecycle management was the entire argument for running a supervisor instead of one process per container. The first revision didn't deliver it.
It cost something concrete during the 2026-08-10 cutover: with two open3e instances fighting over the same MQTT session,
supervisorctl statuswould have shown each service's state and restart count directly. Instead I inferred health fromdocker logsand reported success while the broker was kicking them off in a loop.Change
Unix socket only, inside the container. Nothing outside needs it, and this container runs with host networking, so a TCP listener would be exposed on the host.
The build-time self-test now asserts the section exists and
supervisorctlruns, so it can't regress silently the way it shipped.Note
The MQTT reconnect loop this would have surfaced is already fixed — by #477, not by this PR. The cause was the migration failing to stop the native units, leaving a second open3e on the bus. This is about being able to see such a thing next time.
Verification
Build-time only: the self-test runs inside the image during the kaniko build, so a broken config fails the build rather than being published. Not applied to edge-1 — that needs a rebuild and a
docker compose pull && up.🤖 Generated with Claude Code
🤖 Automated review — model
claude-haiku-4-5-20251001(complexity: trivial), commite3dea169b00d037c612f44f0c170c82cc8861bd0.Summary
This PR adds Unix domain socket support to supervisord in the heating-can container, enabling
supervisorctlto query and control individual services. The change adds three supervisord config sections and build-time validation; no logic or runtime behavior changes beyond enabling the new interface.🟢 Nits
supervisorctl --helpworks, which only verifies the binary exists. It doesn't test thatsupervisorctlcan actually connect to a running supervisord instance (that would require starting supervisord, which isn't necessary at build time). However, full validation happens at runtime when supervisord starts and parses the config—it will fail if the configuration is broken. The current test is appropriate for a build-time gate.Tests
The PR adds build-time validation that:
open3e --help,E3onCANcollect.py --help)supervisord --help)grep -q '^\[supervisorctl\]')supervisorctl --help)Runtime validation occurs when the container starts: supervisord will fail to start if the config has syntax errors. The socket will be created in
/tmp(writable by theopen3euser) with0700permissions (owner-only access), andsupervisorctlcan connect viaunix:///tmp/supervisor.sock.Configuration Details
The three new config sections are syntactically correct:
[unix_http_server](supervisord.conf:21-23): Creates the socket at/tmp/supervisor.sockwith restrictive permissions (0700)./tmpis writable and ephemeral, appropriate for container-scoped runtime state.[rpcinterface:supervisor](supervisord.conf:25-26): Registers the RPC factory, a mandatory prerequisite for supervisorctl.[supervisorctl](supervisord.conf:28-29): Points to the Unix socket using the triple-slash syntax (unix:///tmp/supervisor.sock), which is correct for supervisord.No port is exposed on the host (the config uses only Unix socket, not TCP), which is appropriate given the container runs with
network_mode: hostand should not expose privileged management interfaces.Recommendation
Looks reasonable — no blocking issues; comments above are optional.