Skip to content

fix(pods): self-heal pre-existing pods to pick up the public_html bind#26

Merged
ralyodio merged 1 commit into
mainfrom
fix/pod-public-html-selfheal
Jun 15, 2026
Merged

fix(pods): self-heal pre-existing pods to pick up the public_html bind#26
ralyodio merged 1 commit into
mainfrom
fix/pod-public-html-selfheal

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Follow-up to #25.

Why

The box auto-deploys (self-update timer: pull origin/main → rebuild → systemctl restart agentbbs), but that restart only bounces the daemon. The long-lived per-user pod containers are untouched, and ensure() short-circuits on any existing container — so pods created before #25 would never gain the /home/dev/public_html bind without a manual podman rm. That defeats "everything happens automatically on push."

What

ensure() is now self-healing: if a pod exists but lacks the public_html mount, it recreates it so the bind applies. The named home volume survives rm, so the member's files are kept. Healing only happens when the pod is idle (attached count 0) so a running session is never pulled out from under the user — an unbound pod heals on its next idle attach. New hasMount() inspects the container's mounts.

Net effect: push to main → self-update redeploys within the timer interval → next ssh pod@ recreates the pod with the bind. No manual step.

Build/vet/go test ./internal/pods/ green.

🤖 Generated with Claude Code

The box auto-deploys (self-update timer pulls origin/main, rebuilds, restarts
agentbbs), but `systemctl restart agentbbs` only restarts the daemon — it never
touches the long-lived per-user pod containers. ensure() also short-circuits on
any container that already exists, so pods created before the homepage bind
landed would never gain the /home/dev/public_html mount without a manual
`podman rm`. That defeats the "everything happens automatically on push" goal.

Make ensure() self-healing: when a pod exists but lacks the public_html mount,
recreate it so the bind is applied. The named home volume survives `rm`, so the
member's files are kept. Only heal when the pod is idle (attached count 0) to
avoid pulling a running pod out from under an active session — an unbound pod
heals on its next idle attach. New hasMount() inspects the container's mounts.

Net effect: push to main -> self-update redeploys within the timer interval ->
the next `ssh pod@` recreates the pod with the bind. No manual step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ralyodio ralyodio merged commit 6889918 into main Jun 15, 2026
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

ralyodio added a commit that referenced this pull request Jun 15, 2026
…n box (#27)

The deploy SSHed into the ~458MB droplet and ran `go build` there. The Go
linker's peak memory OOM-killed the build — and with it the sshd serving the
deploy session — surfacing as "Connection closed by remote host" (exit 255).
It was flaky because it tracked momentary memory pressure from the co-resident
ergo/forgejo/tor/podman/agentbbs processes (run #25 passed, #26 failed on
near-identical code).

Build both binaries on the 16GB GitHub runner instead (pure-Go, modernc
sqlite, so CGO_ENABLED=0 static cross-build), scp them to the droplet, and run
setup.sh with SKIP_BUILD=1 so the box never compiles. Arch is detected from
the droplet so amd64/arm64 both work. setup.sh now also skips the Go toolchain
download when SKIP_BUILD=1.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ralyodio added a commit that referenced this pull request Jun 15, 2026
* fix(deploy): build Go binaries on the runner, ship them, SKIP_BUILD on box

The deploy SSHed into the ~458MB droplet and ran `go build` there. The Go
linker's peak memory OOM-killed the build — and with it the sshd serving the
deploy session — surfacing as "Connection closed by remote host" (exit 255).
It was flaky because it tracked momentary memory pressure from the co-resident
ergo/forgejo/tor/podman/agentbbs processes (run #25 passed, #26 failed on
near-identical code).

Build both binaries on the 16GB GitHub runner instead (pure-Go, modernc
sqlite, so CGO_ENABLED=0 static cross-build), scp them to the droplet, and run
setup.sh with SKIP_BUILD=1 so the box never compiles. Arch is detected from
the droplet so amd64/arm64 both work. setup.sh now also skips the Go toolchain
download when SKIP_BUILD=1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(members): member directory + store-and-forward messaging

A members-only hub plugin (the BBS "who") plus user-to-user messaging:

- internal/store: messages table + SendMessage/Inbox/UnreadCount/MarkRead, and
  OnlineUsers (open sessions) for presence. MarkRead is recipient-scoped so a
  member can only clear their own mail.
- plugins/members: directory with online dots + last-seen, a finger-style
  profile view, a minimal compose box, and an inbox that marks read on open.
- ssh msg@host <user> [text]: scriptable CLI to leave a note (body from args or
  stdin), mirroring the existing finger route; "msg"/"message" are reserved.
- hub: "N unread" badge on login (hubMOTD). plugin.Context gains Host for member
  homepage URLs.

Extends the existing finger@ behavior (ssh <name>@host) rather than replacing it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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