Skip to content

feat(admin): build a machine's setup command from the panel - #11

Merged
nickolasdeluca merged 2 commits into
mainfrom
feat/patch-in-builder
Aug 26, 2026
Merged

nickolasdeluca merged 2 commits into
mainfrom
feat/patch-in-builder

Conversation

@nickolasdeluca

Copy link
Copy Markdown
Owner

Connecting a machine meant issuing a credential in one tab, reserving a name in another, then writing the drip command line by hand on the host and getting the flags right. This adds a Patch in tab that builds that line.

Deployment stays two steps. Installing the binary is still whatever that fleet already does — nothing here renders an installer, and nothing depends on how drip arrived on the host.

What it does

The tab asks what the machine serves, then renders the command it runs once the binary is installed, in three platform variants with copy and download:

drip config set --server tunnel.example.com:443 --token PASTE_TOKEN_HERE \
  && drip config tunnel add --name billing --type http --port 8080 --subdomain billing --replace

Decisions worth reviewing

  • The command is rendered in Go, not in the panel. Shell and PowerShell quoting is what keeps a value the operator typed from becoming syntax, so it belongs where it can be tested directly.
  • An existing credential renders PASTE_TOKEN_HERE. Only sha256(secret) is stored, so a token issued earlier cannot be read back. Issuing one from the builder embeds the real token, returned exactly once the way POST /api/clients returns it.
  • Allocations are adopted, not fought over. A name the account already holds is reused and bound to the machine, so rebuilding the command for a deployed machine is safe. One owned by another account, or bound to another machine, is refused.
  • Windows autostart passes --reseed — the step before it rewrote the user config, and service install would otherwise keep a stale %ProgramData% copy — and follows with service start, which install does not do. The Windows script is one command per line, since && needs PowerShell 7.
  • A tcp tunnel gets no --subdomain. The reservation pins the port and the client binds it by asking for nothing.

Testing

12 new tests in api_provision_test.go cover the rendering, the quoting, allocation reuse, cross-account refusal and the credential paths. go vet and go test -race ./... are clean.

Also driven end to end against a local deployment: the generated command configured a machine with an isolated HOME, the tunnel bound its reserved port, and traffic through the public port returned 200.

Not in this PR

Reserving a TCP port still does not check the server's configured port range — the panel has never done this and admin.Deployment does not carry tcp_port_min/max, so a port outside it is accepted and refused later at registration. Recorded in AGENTS.md; fixing it belongs in both reservation paths or neither.

Connecting a machine meant issuing a credential in one tab, reserving a
name in another, then writing the drip command line by hand on the host
and getting the flags right. The panel now builds that line.

The new Patch in tab asks what the machine serves and renders the
command it runs once the drip binary is on it: drip config set, drip
config tunnel add, and optionally the step that leaves the tunnel
running. Deployment stays two steps. Installing the binary is still
whatever that fleet already does, and nothing here renders an installer
or depends on how drip arrived.

POST /api/provision resolves the credential and the allocation and
renders the command in Go, not in the panel. Shell and PowerShell
quoting is what keeps a value the operator typed from becoming syntax,
and that belongs where it can be tested directly rather than in a
template in app.js.

An allocation the account already holds is adopted and bound to the
machine rather than colliding with it, so rebuilding the command for a
machine that is already deployed is safe. One owned by another account,
or bound to another machine, is refused instead of stolen.

An existing credential renders PASTE_TOKEN_HERE, because only
sha256(secret) is stored and its token cannot be read back. Issuing a
credential from the builder embeds the real one, returned exactly once
the way POST /api/clients returns it.

Two platform details the command has to get right on its own: Windows
autostart passes --reseed, since the step before it rewrote the user
config and service install would otherwise keep a stale machine-wide
copy, and follows with service start, which install does not do. A tcp
tunnel gets no --subdomain — the reservation pins the port and the
client binds it by asking for nothing.
A select and an input with identical padding still came out a few pixels
apart, because Chrome forces line-height: normal on a select and ignores
the inherited one. Side by side in a form row that reads as a defect, so
both are pinned to the same box and left to centre their own text.

The native arrow also sits hard against the right edge of the control
and no padding moves it. Both the form selects and the language picker
now draw their own chevron with room around it. A background image
cannot take currentColor, so the glyph is a token with one URI per
theme, defined beside the colours it has to match.
@nickolasdeluca
nickolasdeluca merged commit 86f580a into main Aug 26, 2026
2 checks passed
@nickolasdeluca
nickolasdeluca deleted the feat/patch-in-builder branch August 26, 2026 17:37
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.

1 participant