Skip to content

Commit 95d1afb

Browse files
committed
Merge branch 'feat/files-sftp' into feat/notify-creds
# Conflicts: # cmd/agentbbs/main.go
2 parents c967da9 + 38e4797 commit 95d1afb

45 files changed

Lines changed: 2923 additions & 388 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy.yml

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
name: deploy
22

33
# Fully autonomous, idempotent deploy. On every push to main (or manual
4-
# dispatch) this SSHes to the bbs.profullstack.com droplet and re-runs the
5-
# idempotent provisioner (setup.sh), which pulls origin, rebuilds the Go
6-
# binaries, and restarts the agentbbs service that answers
7-
# `ssh join@bbs.profullstack.com`. Re-running is always safe.
4+
# dispatch) this builds the Go binaries ON THE RUNNER (which has plenty of
5+
# RAM), ships them to the bbs.profullstack.com droplet, and re-runs the
6+
# idempotent provisioner (setup.sh) with SKIP_BUILD=1 so the tiny droplet
7+
# never has to compile. setup.sh still pulls origin, refreshes config/assets,
8+
# and restarts the agentbbs service that answers `ssh join@bbs.profullstack.com`.
9+
# Re-running is always safe.
10+
#
11+
# Why build on the runner: the droplet is a ~458MB box also running ergo,
12+
# forgejo, tor, podman and the live agentbbs. The Go linker's peak memory was
13+
# OOM-killing the build — and with it the sshd serving the deploy session,
14+
# surfacing as "Connection closed by remote host" (exit 255). Compiling on the
15+
# 16GB runner removes that failure mode entirely.
816
#
917
# Required repo secrets (Settings -> Secrets and variables -> Actions):
1018
# DEPLOY_SSH_KEY private key whose public half is in the droplet admin
@@ -30,6 +38,8 @@ jobs:
3038
deploy:
3139
runs-on: ubuntu-latest
3240
steps:
41+
- uses: actions/checkout@v4
42+
3343
- name: Configure SSH
3444
env:
3545
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
@@ -43,7 +53,55 @@ jobs:
4353
chmod 600 ~/.ssh/id_deploy
4454
ssh-keyscan -p "$DEPLOY_PORT" -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null
4555
46-
- name: Provision / redeploy (idempotent)
56+
- name: Detect droplet architecture
57+
id: arch
58+
env:
59+
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
60+
DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }}
61+
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || '2202' }}
62+
run: |
63+
uname_m="$(ssh -i ~/.ssh/id_deploy -p "$DEPLOY_PORT" \
64+
-o BatchMode=yes -o StrictHostKeyChecking=yes \
65+
"${DEPLOY_USER}@${DEPLOY_HOST}" 'uname -m')"
66+
case "$uname_m" in
67+
x86_64|amd64) goarch=amd64 ;;
68+
aarch64|arm64) goarch=arm64 ;;
69+
*) echo "::error::unsupported droplet arch '$uname_m'"; exit 1 ;;
70+
esac
71+
echo "goarch=$goarch" >> "$GITHUB_OUTPUT"
72+
echo "::notice::droplet arch $uname_m -> GOARCH=$goarch"
73+
74+
- uses: actions/setup-go@v5
75+
with:
76+
go-version-file: go.mod
77+
cache: true
78+
79+
- name: Build binaries (on the runner, not the droplet)
80+
env:
81+
GOOS: linux
82+
GOARCH: ${{ steps.arch.outputs.goarch }}
83+
CGO_ENABLED: '0' # pure-Go (modernc sqlite) — static, portable binary
84+
run: |
85+
mkdir -p dist
86+
go build -trimpath -o dist/agentbbs ./cmd/agentbbs
87+
go build -trimpath -o dist/ascii-live ./cmd/ascii-live
88+
file dist/* || true
89+
90+
- name: Ship binaries to the droplet
91+
env:
92+
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
93+
DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }}
94+
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || '2202' }}
95+
run: |
96+
# scp can only name one remote target; copy each binary explicitly.
97+
scp -i ~/.ssh/id_deploy -P "$DEPLOY_PORT" \
98+
-o BatchMode=yes -o StrictHostKeyChecking=yes \
99+
dist/agentbbs "${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/agentbbs-deploy-agentbbs"
100+
scp -i ~/.ssh/id_deploy -P "$DEPLOY_PORT" \
101+
-o BatchMode=yes -o StrictHostKeyChecking=yes \
102+
dist/ascii-live "${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/agentbbs-deploy-ascii-live"
103+
104+
- name: Provision / redeploy (idempotent, SKIP_BUILD=1)
47105
env:
48106
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
49107
DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }}
@@ -76,7 +134,11 @@ jobs:
76134
fi
77135
git -C "$SRC" fetch --depth 1 origin "$BRANCH"
78136
git -C "$SRC" reset --hard "origin/$BRANCH"
79-
exec env BRANCH="$BRANCH" \
137+
# Install the runner-built binaries, then tell setup.sh not to compile.
138+
install -m 0755 /tmp/agentbbs-deploy-agentbbs /usr/local/bin/agentbbs
139+
install -m 0755 /tmp/agentbbs-deploy-ascii-live /usr/local/bin/ascii-live
140+
rm -f /tmp/agentbbs-deploy-agentbbs /tmp/agentbbs-deploy-ascii-live
141+
exec env BRANCH="$BRANCH" SKIP_BUILD=1 \
80142
COINPAY_API_KEY="${COINPAY_API_KEY:-}" \
81143
COINPAY_MERCHANT_ID="${COINPAY_MERCHANT_ID:-}" \
82144
AGENTBBS_QRYPT_ISSUER_KEY="${AGENTBBS_QRYPT_ISSUER_KEY:-}" \

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@
66
*.log
77
.env
88

9+
# Mailu runtime: secrets, local network override, and state
10+
deploy/mailu/mailu.env
11+
deploy/mailu/docker-compose.override.yml
12+
deploy/mailu/data/
13+
deploy/mailu/certs/
14+
915
# Claude Code local worktrees/state
1016
.claude/

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,18 @@ name is an existing AgentBBS member (registration is off — your BBS account *i
123123
your IRC identity):
124124

125125
```bash
126-
# zero-setup: built-in client over SSH (members only)
127-
ssh -t irc@bbs.profullstack.com
128-
# native client — SASL account = your BBS member name
126+
# native TLS client — SASL account = your BBS member name
129127
/connect irc.bbs.profullstack.com 6697
130128
# browser / agent over WebSocket
131129
wss://bbs.profullstack.com/irc
132130
```
133131

134-
`ssh irc@` is a built-in IRC client (`internal/irc`) that authenticates you to
135-
the network automatically — no client to install. Set `IRC=0` to skip the
136-
server. Full details: [`docs/irc.md`](docs/irc.md).
132+
Members connect with **their own IRC client** (or a web client) — there is no
133+
in-BBS `ssh irc@` route. The network is **members-only** and every client must
134+
authenticate with SASL using their BBS account name (any passphrase — membership
135+
is the credential). Set `IRC=0` to skip the server.
136+
137+
Full details: [`docs/irc.md`](docs/irc.md).
137138

138139
### News (Usenet) server
139140

cmd/agentbbs/admin.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,12 @@ func (r *liveReg) List() []admin.Live {
7777
// Kill closes a live session by id. Returns false if it is already gone.
7878
func (r *liveReg) Kill(id int64) bool {
7979
r.mu.Lock()
80+
defer r.mu.Unlock()
8081
e, ok := r.m[id]
81-
r.mu.Unlock()
8282
if !ok {
8383
return false
8484
}
85+
delete(r.m, id)
8586
_ = e.s.Close()
8687
return true
8788
}

0 commit comments

Comments
 (0)