Rebuilds the master's Services and Agents pages per the "Ledger" design: sidebar shell with live nav counts, machine filter and Live indicator; dark/light theme (follows the OS, toggle overrides); embedded Schibsted Grotesk and JetBrains Mono (fontsource 5.3.0, OFL).
Services: Swarm grouped per cluster with its machines under it, offline banner ("Agent offline since HH:MM", browser-local), Simple Icons derived from the image ref (letter circle fallback), existing badges and mode switch restyled, client-side search, machine filter and Icon / Icon + text / Text toggle.
Agents: Online / Offline / Never connected / Revoked, master version in the header with outdated agent versions marked, revoke via popover instead of confirm(), "Add agent" as a three-step panel that completes when the agent says hello (SSE agents).
Store: migration 0005_agent_cluster.sql persists each agent's swarm_cluster_id (needed to list a cluster's managers and its offline state after a master restart).
SSE keepalive is now a named ping event so the Live indicator also shows the connection is alive. revokeAgent now publishes agents.
Workers never report a cluster id (Docker only sets Swarm.Cluster on managers), so their Swarm role shows "—". Fixing that needs a new hello field.
Master binary grows by about 2.16 MB, almost all Simple Icons 16.35.0 path data (gzipped JSON, regenerated with go generate ./internal/master/web/icons). The agent binary does not import it.
Jobs, Job and Service detail pages only get the new shell; their own artboards are out of scope, as are Update buttons, Update all, header stats and the drift badge.
Unchanged: CSRF on every POST, Cache-Control: no-store on the token response, 401 + HX-Redirect without a session, no template.HTML on agent- or user-controlled values, no Node build step.
Test plan
go test ./..., go test -race ./internal/master/...
go test -tags integration ./internal/master/store/ against Postgres 17
docker build
Browser check on fake data: both themes, 1440 px and 390 px, add-agent flow through connect
Services grouping and icons against real stacks and a real Swarm cluster
"Agent offline since" after stopping an agent
Theme toggle, search and machine filter across reloads and mode changes
Add agent end to end with a real agent, Copy buttons
## Summary
- Rebuilds the master's **Services** and **Agents** pages per the "Ledger" design: sidebar shell with live nav counts, machine filter and Live indicator; dark/light theme (follows the OS, toggle overrides); embedded Schibsted Grotesk and JetBrains Mono (fontsource 5.3.0, OFL).
- Services: Swarm grouped per cluster with its machines under it, offline banner ("Agent offline since HH:MM", browser-local), Simple Icons derived from the image ref (letter circle fallback), existing badges and mode switch restyled, client-side search, machine filter and Icon / Icon + text / Text toggle.
- Agents: Online / Offline / Never connected / Revoked, master version in the header with outdated agent versions marked, revoke via popover instead of `confirm()`, "Add agent" as a three-step panel that completes when the agent says hello (SSE `agents`).
- Store: migration `0005_agent_cluster.sql` persists each agent's `swarm_cluster_id` (needed to list a cluster's managers and its offline state after a master restart).
- SSE keepalive is now a named `ping` event so the Live indicator also shows the connection is alive. `revokeAgent` now publishes `agents`.
Closes #18
## Notes
- Workers never report a cluster id (Docker only sets `Swarm.Cluster` on managers), so their Swarm role shows "—". Fixing that needs a new hello field.
- Master binary grows by about 2.16 MB, almost all Simple Icons 16.35.0 path data (gzipped JSON, regenerated with `go generate ./internal/master/web/icons`). The agent binary does not import it.
- Jobs, Job and Service detail pages only get the new shell; their own artboards are out of scope, as are Update buttons, Update all, header stats and the drift badge.
- Unchanged: CSRF on every POST, `Cache-Control: no-store` on the token response, 401 + `HX-Redirect` without a session, no `template.HTML` on agent- or user-controlled values, no Node build step.
## Test plan
- [x] `go test ./...`, `go test -race ./internal/master/...`
- [x] `go test -tags integration ./internal/master/store/` against Postgres 17
- [x] `docker build`
- [x] Browser check on fake data: both themes, 1440 px and 390 px, add-agent flow through connect
- [ ] Services grouping and icons against real stacks and a real Swarm cluster
- [ ] "Agent offline since" after stopping an agent
- [ ] Theme toggle, search and machine filter across reloads and mode changes
- [ ] Add agent end to end with a real agent, Copy buttons
- [ ] Phone at 390 px
https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
The Ledger UI groups Swarm services under their cluster and lists the
cluster's managers, so the master needs to know which cluster an agent
belongs to. RecordHello now stores hello.SwarmClusterID in the new
agents.swarm_cluster_id column.
Claude-Session: https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
Restyles the master UI after the Ledger design: a sidebar with live counts,
a machine filter, a Live box and a light/dark toggle; the overview groups
services per Swarm cluster and machine with icons, search and the
Icon / Icon + text / Text switch; the agents page gets a revoke popover and
an add-agent panel with a connect step. Jobs, Job and Service pages get the
new shell and tokens only.
- fonts and Simple Icons are served by the master (no CDN)
- the overview reads agents too, to show offline banners and cluster managers
- revoking an agent also publishes the agents event
- the SSE keepalive is a named event (ping) the Live box counts
- the master passes its build version to the UI so agents on another
version are marked old
Claude-Session: https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
- icons/gen writes to the current directory so `go generate
./internal/master/web/icons` works (output verified byte-identical)
- share the registry-host check as imageref.IsRegistryHost (with test)
- agents fragment refreshes the "N machines" header count out of band
- ignore the saved machine filter at <=760px where the machine list is hidden
- aria-current on the active machine filter link, aria-pressed on theme toggle
- CLAUDE.md: icons package, SSE ping event, swarm_cluster_id
Claude-Session: https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
- Move the .agent @container rule after the base .agent rules. A container
query adds no specificity, so the earlier position always lost and rows
kept six fixed columns, spilling Revoke buttons onto the open drawer.
- Give .drawer min-width:0 so the compose snippet scrolls inside its <pre>
instead of widening the drawer past the viewport at 390px.
Claude-Session: https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
Conflicts:
- CLAUDE.md: keep both sets of gotchas (drift/merge from main, SSE ping and
swarm_cluster_id from this branch).
- internal/imageref/imageref_test.go: keep both TestIsDigest (main) and
TestIsRegistryHost (this branch).
- internal/master/web/server.go: take main's renderServiceError and make it
render through s.page, so service error pages get the sidebar nav data.
Also renumber this branch's migration 0005_agent_cluster.sql to
0006_agent_cluster.sql, after main's 0005_drift_merge.sql.
Claude-Session: https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
shcizo
merged commit ec28d1e027 into main2026-10-13 09:20:21 +00:00
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.
Summary
confirm(), "Add agent" as a three-step panel that completes when the agent says hello (SSEagents).0005_agent_cluster.sqlpersists each agent'sswarm_cluster_id(needed to list a cluster's managers and its offline state after a master restart).pingevent so the Live indicator also shows the connection is alive.revokeAgentnow publishesagents.Closes #18
Notes
Swarm.Clusteron managers), so their Swarm role shows "—". Fixing that needs a new hello field.go generate ./internal/master/web/icons). The agent binary does not import it.Cache-Control: no-storeon the token response, 401 +HX-Redirectwithout a session, notemplate.HTMLon agent- or user-controlled values, no Node build step.Test plan
go test ./...,go test -race ./internal/master/...go test -tags integration ./internal/master/store/against Postgres 17docker buildhttps://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ