Skip to content

feat: a submission page at /ui, reachable over the tailnet - #4

Merged
cyrilcaoyang merged 2 commits into
mainfrom
feat/submission-ui
Sep 7, 2026
Merged

feat: a submission page at /ui, reachable over the tailnet#4
cyrilcaoyang merged 2 commits into
mainfrom
feat/submission-ui

Conversation

@cyrilcaoyang

Copy link
Copy Markdown
Collaborator

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 page

One 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:

  • No build step and no external resources — no CDN, no npm, no bundler — served from the same origin as the API it calls, so no CORS exemption and it works on an isolated lab network.
  • It can do nothing the API would refuse. No state of its own, only public endpoints. Approve is offered only on a queued job, mirroring the server's rule rather than duplicating logic — never advertise a refusal.
  • Everything user-supplied reaches the DOM via textContent. Filenames and submitter names are attacker-controlled text; interpolating them as markup would be an XSS in a lab tool.
  • A check that could not run renders as a dash, never a tick.
  • The page states plainly that there is no sign-in and that submitting reaches no printer.

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.10.0.0.0

This 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 in ac-organic-lab/equipment.yaml are on loopback, since the aggregator runs on the same host), while a person opening /ui needs 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_SETUP has device services bind 0.0.0.0 with 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 /submissions has 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 /ui through the Caddy edge like the camera gateway, which would also put it behind ac_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

  • Not visually verified. There is no browser on the deploy host and installing one there was not warranted, so only the HTML structure and the script's syntax (node --check) were checked. Please look at it in a real browser.
  • The comment sits above ExecStart, not inside its line continuation: a # between continuation lines is not reliably a comment across systemd versions. Both units pass systemd-analyze verify.
  • Installing needs root — /etc/systemd/system/bambu-server.service is a copy of the repo file, not a symlink.
  • The durable home for this UI is probably the lab dashboard once ac_auth makes requested_by a real identity. This is the interim surface. The companion dashboard link is AccelerationConsortium/ac-organic-lab#feat/bambu-submission-link.

uv run ruff check . and uv run pytest -q (130 tests) pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UQvsfEeDitEyNzbCwrcEdD

YoyoLiuuu and others added 2 commits September 7, 2026 00:00
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
@cyrilcaoyang
cyrilcaoyang merged commit 3da8266 into main Sep 7, 2026
1 check passed
@cyrilcaoyang
cyrilcaoyang deleted the feat/submission-ui branch September 7, 2026 04:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants