feat: a submission page at /ui, reachable over the tailnet - #4
Merged
Conversation
The pipeline shipped with no human-facing surface. The design assumed the lab dashboard would render its endpoints, so a UI was never in its scope, which left curl and Swagger as the only way in. One static file (`src/bambu_server/static/index.html`): pick a machine and see what you are targeting (plate size, nozzle, chamber, limits, loaded trays), upload a file, read the per-check verdict, and work the queue with finish times and Approve / Cancel. Deliberate properties: - No build step and no external resources -- no CDN, no npm, no bundler -- and served from the same origin as the API it calls, so it needs no CORS exemption and works on an isolated lab network. - It holds no state and calls only public endpoints, so it can do nothing the API would refuse. It offers Approve only on a `queued` job, mirroring the server's own rule: never advertise an action that would be refused. - Everything user-supplied reaches the DOM through `textContent`. Filenames and submitter names are attacker-controlled text and are never interpolated as markup. - A check that could not run renders as a dash, never a tick. - The page says plainly that there is no sign-in and that submitting reaches no printer. Excluded from the OpenAPI schema: it is a page, not part of the contract a machine client reads. Shipped as package data so a non-editable install serves it too, not just a checkout-based deploy. Not visually verified -- there is no browser on this host, so only the HTML structure and the script's syntax were checked. The durable home is probably the lab dashboard once `ac_auth` makes `requested_by` a real identity; this is the interim surface. `uv run ruff check .` and `uv run pytest -q` (130 tests) pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQvsfEeDitEyNzbCwrcEdD
Two readers need this service on addresses that cannot be the same one. The dashboard aggregator polls it at 127.0.0.1:8012 (ac-organic-lab/equipment.yaml has all three Bambu entries on loopback, since the aggregator runs on this host), while a person opening /ui reaches it over the tailnet at 100.64.254.6:8012 -- and loopback in a browser is the visitor's own machine. A loopback-only bind therefore serves the aggregator and nobody else, which is why the submission page could not be seen. This matches the fleet's documented posture: DEVICE_PC_SETUP has device services bind 0.0.0.0 with access gated by Tailscale ACLs rather than a local firewall, so the Bambu gateway was the outlier here. What it widens, stated plainly: port 8012 is now reachable on every interface this host has -- tailnet and lab LAN -- and POST /submissions has no application-level auth, so anyone who can reach the port can queue a job. The tighter shape, if that ever matters, is to keep loopback and front /ui through the Caddy edge like the camera gateway, which would also put it behind ac_auth. The generic unit template stays on loopback as the conservative default for a fresh host, with a pointer to the note here. Note the comment sits above ExecStart, not inside its line continuation: a '#' between continuation lines is not reliably treated as a comment across systemd versions and can end up in the command line. Both units pass `systemd-analyze verify`. Installing this needs root -- /etc/systemd/system/bambu-server.service is a copy of this file, not a symlink, so it must be re-copied and daemon-reloaded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQvsfEeDitEyNzbCwrcEdD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The pipeline shipped with no human-facing surface and bound to loopback, so in practice nobody could use it. Two commits fix each half.
/ui— the submission pageOne static file (
src/bambu_server/static/index.html). Pick a machine and see what you are targeting (plate size, nozzle, chamber, limits, loaded AMS trays, profile warnings), upload a file, read the per-check verdict as a checklist, and work the queue with finish times and Approve / Cancel.Deliberate properties:
queuedjob, mirroring the server's rule rather than duplicating logic — never advertise a refusal.textContent. Filenames and submitter names are attacker-controlled text; interpolating them as markup would be an XSS in a lab tool.Excluded from the OpenAPI schema (it is a page, not contract) and shipped as package data so a non-editable install serves it too.
--host 127.0.0.1→0.0.0.0This is why the page could not be seen. Two readers need addresses that cannot be the same one: the dashboard aggregator polls this service at
127.0.0.1:8012(all three Bambu entries inac-organic-lab/equipment.yamlare on loopback, since the aggregator runs on the same host), while a person opening/uineeds the tailnet address — and loopback in a browser is the visitor's own machine. Loopback-only served the aggregator and nobody else.It also aligns the gateway with the fleet:
DEVICE_PC_SETUPhas device services bind0.0.0.0with access gated by Tailscale ACLs rather than a local firewall, so this one was the outlier.What it widens, stated plainly: port 8012 becomes reachable on every interface the host has — tailnet and lab LAN — and
POST /submissionshas no application-level auth, so anyone who can reach the port can queue a job. The tighter shape, if that matters, is keeping loopback and fronting/uithrough the Caddy edge like the camera gateway, which would also put it behindac_auth. Noted in the unit rather than done here.The generic unit template stays on loopback as the conservative default for a fresh host.
Reviewer notes
node --check) were checked. Please look at it in a real browser.ExecStart, not inside its line continuation: a#between continuation lines is not reliably a comment across systemd versions. Both units passsystemd-analyze verify./etc/systemd/system/bambu-server.serviceis a copy of the repo file, not a symlink.ac_authmakesrequested_bya real identity. This is the interim surface. The companion dashboard link isAccelerationConsortium/ac-organic-lab#feat/bambu-submission-link.uv run ruff check .anduv run pytest -q(130 tests) pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01UQvsfEeDitEyNzbCwrcEdD