eleven / messenger Public

deploy: add a script to deploy the current branch to a router-box instance (#274)

* deploy: add a script to deploy the current branch to a router-box instance

Builds whatever branch is checked out, stages it, and deploys it to a
single named instance (default james.elevenmessenger.com) on the
self-hosted router box — never the whole fleet. Also ensures the
instance has its default bot sidecar running, deterministic-only (no
ANTHROPIC_API_KEY) unless one is passed in, so Eleven welcomes members
and answers slash commands out of the box; idempotent, since re-running
add-bot would mint fresh one-time invites and orphan the existing
identities.

Works around a bug in the box's current control binary: add-bot's
MkdirAll leaves the bot's session dir root-owned, but the hardened
bot unit runs as the unprivileged `lchat` user and can't write into
it, so a freshly-added bot crash-loops on EACCES and burns its Claude
invite before the fix (already merged, commit 8e20692) reaches the
box's /opt/lchat/lchat. Pre-creating the dir with the right owner
sidesteps it, and is a harmless no-op once that binary is updated.

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

* deploy: default deploy-to-box.sh to team.elevenmessenger.com

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

* deploy: tidy up script names and locations

- deploy-instance.sh -> deploy/miren.sh (the Miren-cluster deploy path)
- deploy/deploy-to-box.sh -> deploy/go-router.sh (the self-hosted router
  box path, matching miren.sh's naming)

Updates every reference across the repo (scripts, docs, README, CLAUDE.md,
app.toml comments) so nothing points at the old paths.

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

* deploy: move the remaining Miren sibling-service scripts under deploy/miren/

deploy-agent.sh -> deploy/miren/agent.sh
deploy-home.sh  -> deploy/miren/home.sh
deploy-push.sh  -> deploy/miren/push.sh

Groups every Miren-cluster deploy script together (alongside deploy/miren.sh
for the main instance), and updates all references across docs, Dockerfiles,
and app.toml comments.

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

---------
Co-authored-by: Claude <noreply@anthropic.com>
James Adam 3dc84a5606c3e55c8fda3ae6c0ecc98643b75555 parent b845cb0
16 files changed, +129 −47
  • .miren/agent.app.toml +2 −2
  • CLAUDE.md +3 −3
  • README.md +1 −1
  • deploy/go-router.sh +82 −0
  • deploy-instance.sh => deploy/miren.sh +5 −5
  • deploy-agent.sh => deploy/miren/agent.sh +4 −4
  • deploy-home.sh => deploy/miren/home.sh +7 −7
  • deploy-push.sh => deploy/miren/push.sh +8 −8
  • docs/bots.md +2 −2
  • docs/instance-router.md +1 −1
  • docs/push-relay.md +2 −2
  • home/Dockerfile +1 −1
  • home/README.md +2 −2
  • home/app.toml +4 −4
  • push/Dockerfile +1 −1
  • push/app.toml +4 −4
diff --git a/.miren/agent.app.toml b/.miren/agent.app.toml
index 68a0a63..c36877a 100644
--- a/.miren/agent.app.toml
+++ b/.miren/agent.app.toml
@@ -1,5 +1,5 @@
# Miren config for a bot sidecar app (one per lchat instance). Templated by
-# deploy-agent.sh (__APP__ -> "<instance>-bot"). This is a worker, not a public
+# deploy/miren/agent.sh (__APP__ -> "<instance>-bot"). This is a worker, not a public
# site: it has a service+port only so Miren can health-check it (see the health
# endpoint in agent.mjs); no route is set, so it has no public hostname.
name = "__APP__"
@@ -37,7 +37,7 @@ value = "/data/announcer-session.json"
key = "LCHAT_ELEVEN_SESSION"
value = "/data/eleven-session.json"
-# Set at deploy time via `-e` (see deploy-agent.sh), NOT declared here:
+# Set at deploy time via `-e` (see deploy/miren/agent.sh), NOT declared here:
# LCHAT_BASE the instance to connect to, e.g. https://test-bots.luan.chat
# ANTHROPIC_API_KEY billing credential — lives only in this app's env
# LCHAT_INVITE one-time invite #fragment; used only on first boot
diff --git a/CLAUDE.md b/CLAUDE.md
index 308c886..ac19c11 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -253,7 +253,7 @@ Add tests as you go. Go handler tests use `newTestServer(t)` which returns
Deployed as a single binary on a container platform. Each chat is a
**fully-isolated instance** — its own app, disk, admin token, and subdomain.
-`deploy-instance.sh` is the committed deploy script: it templates a per-instance
+`deploy/miren.sh` is the committed deploy script: it templates a per-instance
config with a unique app + disk name and sets the route.
> Environment specifics — server, platform/cluster name, the instance↔subdomain
@@ -262,11 +262,11 @@ config with a unique app + disk name and sets the route.
Principles that hold regardless of environment:
-- **Deploys are data-safe.** A plain redeploy (what `deploy-instance.sh` runs)
+- **Deploys are data-safe.** A plain redeploy (what `deploy/miren.sh` runs)
**preserves the disk**. **Never delete the app to update it** — that wipes the
disk; delete only to intentionally reset an instance. DB migrations are additive
(`CREATE TABLE IF NOT EXISTS`), so new code over old data is safe.
-- **Reuse an existing instance's admin token on redeploy** — `deploy-instance.sh`
+- **Reuse an existing instance's admin token on redeploy** — `deploy/miren.sh`
mints a new one if the token arg is omitted, which would invalidate saved links.
- **Env contract:** the app reads `LCHAT_ADMIN_TOKEN`, `LCHAT_DB`, `ADDR`. Use the
`LCHAT_`-prefix deliberately (a bare `ADMIN_TOKEN` can be auto-provisioned/
diff --git a/README.md b/README.md
index 139c373..a2c0b72 100644
--- a/README.md
+++ b/README.md
@@ -248,7 +248,7 @@ DNS resolves, Miren provisions TLS automatically.
keys, group name) all survive. **Do not `miren app delete` to update** — that
deletes the disk and wipes everything; use it only to intentionally reset.
-**More instances from one repo:** `./deploy-instance.sh <name> <name>.luan.chat`
+**More instances from one repo:** `./deploy/miren.sh <name> <name>.luan.chat`
gives each its own app + disk + token + route (re-run it to update an instance;
it preserves that instance's data too).
diff --git a/deploy/go-router.sh b/deploy/go-router.sh
new file mode 100755
index 0000000..de2ecd5
--- /dev/null
+++ b/deploy/go-router.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+# Build whatever branch is currently checked out and deploy it to a single
+# instance on the self-hosted router box (versioned, side-by-side deploys —
+# see docs/instance-router.md and CLAUDE.local.md "Deploying"). Health-checked:
+# `lchat deploy` itself verifies /api/version's build id after restarting and
+# auto-rolls back on a mismatch, so a bad build never stays live. Also ensures
+# the instance has its default bot sidecar (Claude/Eleven/Announcer) running —
+# deterministic-only (no ANTHROPIC_API_KEY) unless you pass one in — so Eleven
+# always welcomes new members and answers slash commands out of the box.
+#
+# Usage: ./deploy/go-router.sh [host]
+# ./deploy/go-router.sh # host defaults to team.elevenmessenger.com
+# ./deploy/go-router.sh test.luan.chat
+# SKIP_BOT=1 ./deploy/go-router.sh # skip the bot-ensure step
+# ANTHROPIC_API_KEY=sk-ant-... ./deploy/go-router.sh # AI-enabled instead
+#
+# Never deploys the whole fleet (`--all`) — always one explicit host, so this
+# script can never affect an instance other than the one you asked for.
+set -euo pipefail
+
+HOST="${1:-team.elevenmessenger.com}"
+BOX="${LCHAT_BOX:-root@89.167.122.227}"
+SKIP_BOT="${SKIP_BOT:-}"
+ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}"
+
+REPO="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$REPO"
+
+BRANCH="$(git rev-parse --abbrev-ref HEAD)"
+VER="$(git rev-parse --short HEAD)"
+if ! git diff --quiet || ! git diff --cached --quiet; then
+ VER="${VER}-dirty"
+ echo "Warning: working tree has uncommitted changes — building anyway (version tagged '$VER')." >&2
+fi
+
+OUT="$(mktemp -t lchat-linux-amd64.XXXXXX)"
+trap 'rm -f "$OUT"' EXIT
+
+echo "Building branch '$BRANCH' ($VER) for linux/amd64..."
+OUT="$OUT" ./deploy/build-linux.sh "$VER"
+
+echo "Uploading to $BOX:/opt/lchat/incoming..."
+scp -q "$OUT" "$BOX:/opt/lchat/incoming"
+
+echo "Staging on the box..."
+STAGE_OUT="$(ssh "$BOX" '/opt/lchat/lchat stage -file /opt/lchat/incoming')"
+echo " $STAGE_OUT"
+STAGED_VER="$(echo "$STAGE_OUT" | awk '{print $2}')"
+if [ "$STAGED_VER" != "$VER" ]; then
+ echo "go-router: staged version '$STAGED_VER' doesn't match built version '$VER' — aborting" >&2
+ exit 1
+fi
+
+echo "Deploying $STAGED_VER to $HOST (health-checked; auto-rolls back on a build mismatch)..."
+ssh "$BOX" "/opt/lchat/lchat deploy '$HOST' --version '$STAGED_VER'"
+
+if [ -n "$SKIP_BOT" ]; then
+ echo "SKIP_BOT set — leaving the bot sidecar alone."
+elif ssh "$BOX" "test -f /data/${HOST}-bot.env"; then
+ echo "Bot sidecar already provisioned for $HOST — leaving it alone (add-bot mints fresh, one-time invites, so re-running it would orphan the existing Claude/Eleven/Announcer identities)."
+else
+ echo "No bot sidecar yet for $HOST — adding one (deterministic-only unless \$ANTHROPIC_API_KEY is set)..."
+ # Work around a known bug in the box's current control binary: add-bot's
+ # MkdirAll leaves the session dir root-owned, but the bot unit runs as the
+ # unprivileged `lchat` user (post-hardening) and can't write into it — every
+ # freshly-added bot crash-loops with EACCES, burning its one-time Claude
+ # invite before anyone can intervene (hit this firsthand adding Eleven here).
+ # Fixed upstream in 8e20692, not yet deployed to /opt/lchat/lchat's control
+ # binary on the box. Pre-creating the dir with the right owner is a no-op
+ # once that binary is updated, so this is safe to leave in permanently.
+ ssh "$BOX" "mkdir -p /data/${HOST}-bot && chown lchat:lchat /data/${HOST}-bot && chmod 750 /data/${HOST}-bot"
+ if [ -n "$ANTHROPIC_API_KEY" ]; then
+ ssh "$BOX" "ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY' /opt/lchat/lchat add-bot '$HOST'"
+ else
+ ssh "$BOX" "/opt/lchat/lchat add-bot '$HOST'"
+ fi
+fi
+
+echo
+echo "✅ $HOST is now running $STAGED_VER (branch '$BRANCH')"
+echo " Recover the admin link anytime: ssh $BOX /opt/lchat/lchat admin-link $HOST"
+echo " Step one instance back: ssh $BOX /opt/lchat/lchat rollback $HOST"
diff --git a/deploy-instance.sh b/deploy/miren.sh
similarity index 90%
rename from deploy-instance.sh
rename to deploy/miren.sh
index bb6b273..91070cf 100755
--- a/deploy-instance.sh
+++ b/deploy/miren.sh
@@ -3,19 +3,19 @@
# app.toml — its own Miren app, disk, admin token, and route. Keeps git clean:
# no per-instance config files.
#
-# Usage: ./deploy-instance.sh <instance> <hostname> [admin-token]
-# ./deploy-instance.sh paja paja.luan.chat
+# Usage: ./deploy/miren.sh <instance> <hostname> [admin-token]
+# ./deploy/miren.sh paja paja.luan.chat
#
# Miren's app.toml has no variable interpolation, so we template a throwaway copy
# in a temp dir (unique app name + unique disk name) and deploy from there.
set -euo pipefail
-INSTANCE="${1:?usage: deploy-instance.sh <instance> <hostname> [admin-token]}"
-HOST="${2:?usage: deploy-instance.sh <instance> <hostname> [admin-token]}"
+INSTANCE="${1:?usage: deploy/miren.sh <instance> <hostname> [admin-token]}"
+HOST="${2:?usage: deploy/miren.sh <instance> <hostname> [admin-token]}"
TOKEN="${3:-$(openssl rand -base64 32 | tr -d '\n' | tr '+/' '-_' | tr -d '=')}"
CLUSTER="${MIREN_CLUSTER:-luanchat}"
-REPO="$(cd "$(dirname "$0")" && pwd)"
+REPO="$(cd "$(dirname "$0")/.." && pwd)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
diff --git a/deploy-agent.sh b/deploy/miren/agent.sh
similarity index 90%
rename from deploy-agent.sh
rename to deploy/miren/agent.sh
index 154fcd5..a51eb44 100755
--- a/deploy-agent.sh
+++ b/deploy/miren/agent.sh
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
# Deploy the bot sidecar as its own Miren app, alongside (but separate from) an
-# lchat instance. Mirrors deploy-instance.sh: templates a throwaway app.toml so
+# lchat instance. Mirrors deploy/miren.sh: templates a throwaway app.toml so
# git stays clean.
#
# Usage:
# LCHAT_BASE=https://<host> [ANTHROPIC_API_KEY=sk-ant-...] [LCHAT_INVITE=<T>] \
-# ./deploy-agent.sh <instance>
+# ./deploy/miren/agent.sh <instance>
#
# <instance> -> app "<instance>-bot", disk "<instance>-bot-data"
#
@@ -18,12 +18,12 @@
# and persists its session to the /data disk; later deploys ignore it).
set -euo pipefail
-INSTANCE="${1:?usage: deploy-agent.sh <instance>}"
+INSTANCE="${1:?usage: deploy/miren/agent.sh <instance>}"
: "${LCHAT_BASE:?set LCHAT_BASE (e.g. https://test-bots.luan.chat)}"
CLUSTER="${MIREN_CLUSTER:-luanchat}"
APP="${INSTANCE}-bot"
-REPO="$(cd "$(dirname "$0")" && pwd)"
+REPO="$(cd "$(dirname "$0")/../.." && pwd)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
diff --git a/deploy-home.sh b/deploy/miren/home.sh
similarity index 85%
rename from deploy-home.sh
rename to deploy/miren/home.sh
index 5ab8fe6..787a514 100755
--- a/deploy-home.sh
+++ b/deploy/miren/home.sh
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
# Deploy the home server (home/ — the anonymous passkey-gated backup store) as
-# its own Miren app. Mirrors deploy-instance.sh: templates a throwaway app.toml
+# its own Miren app. Mirrors deploy/miren.sh: templates a throwaway app.toml
# in a temp dir so git stays clean; a plain redeploy PRESERVES the disk (never
# delete the app to update it — that wipes users' backups).
#
-# Usage: ./deploy-home.sh <instance> <hostname>
-# ./deploy-home.sh home home.elevenmessenger.com
+# Usage: ./deploy/miren/home.sh <instance> <hostname>
+# ./deploy/miren/home.sh home home.elevenmessenger.com
#
# There is no admin token: the home server has no admin surface. The only
# per-instance knob is LCHAT_HOME_ORIGIN (the WebAuthn Relying Party origin) —
@@ -13,11 +13,11 @@
# existing passkeys (they're bound to the RP ID), so pick it once.
set -euo pipefail
-INSTANCE="${1:?usage: deploy-home.sh <instance> <hostname>}"
-HOST="${2:?usage: deploy-home.sh <instance> <hostname>}"
+INSTANCE="${1:?usage: deploy/miren/home.sh <instance> <hostname>}"
+HOST="${2:?usage: deploy/miren/home.sh <instance> <hostname>}"
CLUSTER="${MIREN_CLUSTER:-luanchat}"
-REPO="$(cd "$(dirname "$0")" && pwd)"
+REPO="$(cd "$(dirname "$0")/../.." && pwd)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
@@ -25,7 +25,7 @@ trap 'rm -rf "$TMP"' EXIT
# The Dockerfile and app.toml live under home/ (keeping the messenger's
# defaults untouched); stage them where Miren expects them.
cp -R "$REPO"/. "$TMP"/
-# Trim the context before Miren uploads it (same reasoning as deploy-instance.sh:
+# Trim the context before Miren uploads it (same reasoning as deploy/miren.sh:
# Miren uploads this dir FIRST, so a fat working tree makes every deploy slow).
# Drop what the home image never needs: the native ios/ build output (~700MB),
# node_modules, VCS/editor/agent dirs, the local binary, and *.db — the local
diff --git a/deploy-push.sh b/deploy/miren/push.sh
similarity index 84%
rename from deploy-push.sh
rename to deploy/miren/push.sh
index 23abec5..cc44833 100755
--- a/deploy-push.sh
+++ b/deploy/miren/push.sh
@@ -1,24 +1,24 @@
#!/usr/bin/env bash
# Deploy the push relay (push/ — the central APNs signer) as its own Miren app.
-# Mirrors deploy-home.sh: templates a throwaway app.toml in a temp dir so git
+# Mirrors deploy/miren/home.sh: templates a throwaway app.toml in a temp dir so git
# stays clean; a plain redeploy PRESERVES the disk (which holds the minted
# per-instance tokens — never delete the app to update it).
#
# The Apple .p8 signing key lives ONLY on this app. It is read from the caller's
-# env (LCHAT_APNS_KEY + _KEY_ID / _TEAM_ID / _TOPIC — same names deploy-instance.sh
+# env (LCHAT_APNS_KEY + _KEY_ID / _TEAM_ID / _TOPIC — same names deploy/miren.sh
# uses) and passed with `-e` (Miren doesn't inject `-s` sensitive vars into the
# container). Pass LCHAT_APNS_KEY base64-packed; the relay decodes it.
#
-# Usage: ./deploy-push.sh <instance> <hostname> [admin-token]
-# ./deploy-push.sh push push.elevenmessenger.com
+# Usage: ./deploy/miren/push.sh <instance> <hostname> [admin-token]
+# ./deploy/miren/push.sh push push.elevenmessenger.com
#
# Reuse the admin token on redeploy (omitting it mints a new one, which would
# invalidate the saved token). Recover it with:
# miren env get LCHAT_ADMIN_TOKEN -u -a push -C <cluster>
set -euo pipefail
-INSTANCE="${1:?usage: deploy-push.sh <instance> <hostname> [admin-token]}"
-HOST="${2:?usage: deploy-push.sh <instance> <hostname> [admin-token]}"
+INSTANCE="${1:?usage: deploy/miren/push.sh <instance> <hostname> [admin-token]}"
+HOST="${2:?usage: deploy/miren/push.sh <instance> <hostname> [admin-token]}"
TOKEN="${3:-$(openssl rand -base64 32 | tr -d '\n' | tr '+/' '-_' | tr -d '=')}"
CLUSTER="${MIREN_CLUSTER:-luanchat}"
@@ -26,12 +26,12 @@ for v in LCHAT_APNS_KEY LCHAT_APNS_KEY_ID LCHAT_APNS_TEAM_ID LCHAT_APNS_TOPIC; d
[ -n "${!v:-}" ] || { echo "error: $v must be set (the .p8 signing config lives only on the relay)" >&2; exit 1; }
done
-REPO="$(cd "$(dirname "$0")" && pwd)"
+REPO="$(cd "$(dirname "$0")/../.." && pwd)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
# Build context = the source, with instance-specific app + disk name. Trim the
-# upload context (same reasoning as deploy-home.sh): drop the native ios/ build
+# upload context (same reasoning as deploy/miren/home.sh): drop the native ios/ build
# output (~700MB), node_modules, VCS/editor/agent dirs, the local binary, and
# *.db — the local SQLite holding data, which must never leave the machine.
cp -R "$REPO"/. "$TMP"/
diff --git a/docs/bots.md b/docs/bots.md
index 1e015c5..45ce18a 100644
--- a/docs/bots.md
+++ b/docs/bots.md
@@ -177,11 +177,11 @@ but one process, one host, one API key.
### Deploy (built and proven on `test-bots`)
Files: `Dockerfile.agent` (Node), `.miren/agent.app.toml` (templated per instance),
-`deploy-agent.sh`. Deploy:
+`deploy/miren/agent.sh`. Deploy:
```sh
LCHAT_BASE=https://<host> ANTHROPIC_API_KEY=sk-ant-... LCHAT_INVITE=<T> \
- ./deploy-agent.sh <instance> # -> app "<instance>-bot"
+ ./deploy/miren/agent.sh <instance> # -> app "<instance>-bot"
```
The app has a `[services.web]` with a health port (no route → no public hostname),
diff --git a/docs/instance-router.md b/docs/instance-router.md
index 62dface..66d4966 100644
--- a/docs/instance-router.md
+++ b/docs/instance-router.md
@@ -481,7 +481,7 @@ touching anything real.
real router.
- Deploy: a socket swap drains without dropping an in-flight request (integration).
-## Deploy tooling (replaces deploy-instance.sh)
+## Deploy tooling (replaces deploy/miren.sh)
- `lchat bootstrap` — one-time box setup: write systemd units, make dirs, start
the router (no toolchain needed — just the copied binary).
diff --git a/docs/push-relay.md b/docs/push-relay.md
index 6c1785b..65ea6d9 100644
--- a/docs/push-relay.md
+++ b/docs/push-relay.md
@@ -7,7 +7,7 @@ the design note; the code is the source of truth.
Native iOS push (`apns.go`) needs Apple's `.p8` signing key. Every lchat
instance pushes to the **same** Apple app (`com.elevenmessenger.messenger`) via
-the **same** team's key, so `deploy-instance.sh` used to copy that `.p8` into
+the **same** team's key, so `deploy/miren.sh` used to copy that `.p8` into
**every** instance's env. Two consequences:
- **Wide blast radius.** The Apple signing key sat on every box. Leak one
@@ -108,7 +108,7 @@ boot silently disabled. An empty token table is fine (mint after first deploy).
## Deploy
-`./deploy-push.sh push push.elevenmessenger.com` (mirrors `deploy-home.sh`;
+`./deploy/miren/push.sh push push.elevenmessenger.com` (mirrors `deploy/miren/home.sh`;
data-safe redeploys preserve the disk — never delete the app to update it). The
`.p8` + admin token are read from the caller's env and passed with `-e`. DNS
needs an explicit `push.elevenmessenger.com` A record (no wildcard on that zone,
diff --git a/home/Dockerfile b/home/Dockerfile
index b26a0ac..7cd56ab 100644
--- a/home/Dockerfile
+++ b/home/Dockerfile
@@ -1,5 +1,5 @@
# The home server: single static Go binary, built from the repo root so the
-# shared go.mod resolves. deploy-home.sh copies this file to the build context
+# shared go.mod resolves. deploy/miren/home.sh copies this file to the build context
# root as "Dockerfile.home".
# ---- build ----
diff --git a/home/README.md b/home/README.md
index ec21eb2..f313f3c 100644
--- a/home/README.md
+++ b/home/README.md
@@ -86,5 +86,5 @@ go test ./home
`LCHAT_HOME_ORIGIN` is the public origin (the WebAuthn RP identity) — pick the
hostname once; changing it orphans existing passkeys. Deploy with
-`./deploy-home.sh <instance> <hostname>` (data-safe redeploys, same rules as
-`deploy-instance.sh`: never delete the app to update it).
+`./deploy/miren/home.sh <instance> <hostname>` (data-safe redeploys, same rules as
+`deploy/miren.sh`: never delete the app to update it).
diff --git a/home/app.toml b/home/app.toml
index 273bf03..41f0adf 100644
--- a/home/app.toml
+++ b/home/app.toml
@@ -1,11 +1,11 @@
-# Miren deployment config template for the home server (see deploy-home.sh,
+# Miren deployment config template for the home server (see deploy/miren/home.sh,
# which copies this to .miren/app.toml with the instance name substituted —
-# mirrors how deploy-instance.sh templates the messenger's app.toml).
+# mirrors how deploy/miren.sh templates the messenger's app.toml).
name = "home"
[build]
-# deploy-home.sh places home/Dockerfile at the context root under this name.
+# deploy/miren/home.sh places home/Dockerfile at the context root under this name.
dockerfile = "Dockerfile.home"
[services.web]
@@ -29,6 +29,6 @@ key = "LCHAT_HOME_DB"
value = "/data/home.db"
# LCHAT_HOME_ORIGIN (the public https origin — the WebAuthn RP identity) is set
-# at deploy time by deploy-home.sh via `miren deploy -e`, since it depends on
+# at deploy time by deploy/miren/home.sh via `miren deploy -e`, since it depends on
# the hostname being routed. There is deliberately NO admin token: the server
# has no admin surface, and no key material to protect.
diff --git a/push/Dockerfile b/push/Dockerfile
index e0cb103..40dfffd 100644
--- a/push/Dockerfile
+++ b/push/Dockerfile
@@ -1,5 +1,5 @@
# The push relay: single static Go binary, built from the repo root so the
-# shared go.mod resolves. deploy-push.sh copies this file to the build context
+# shared go.mod resolves. deploy/miren/push.sh copies this file to the build context
# root as "Dockerfile.push".
# ---- build ----
diff --git a/push/app.toml b/push/app.toml
index 240389e..b115a56 100644
--- a/push/app.toml
+++ b/push/app.toml
@@ -1,11 +1,11 @@
-# Miren deployment config template for the push relay (see deploy-push.sh, which
+# Miren deployment config template for the push relay (see deploy/miren/push.sh, which
# copies this to .miren/app.toml with the instance name substituted — mirrors
-# how deploy-home.sh / deploy-instance.sh template their app.toml).
+# how deploy/miren/home.sh / deploy/miren.sh template their app.toml).
name = "push"
[build]
-# deploy-push.sh places push/Dockerfile at the context root under this name.
+# deploy/miren/push.sh places push/Dockerfile at the context root under this name.
dockerfile = "Dockerfile.push"
[services.web]
@@ -31,4 +31,4 @@ key = "LCHAT_PUSH_DB"
value = "/data/push.db"
# LCHAT_ADMIN_TOKEN and the LCHAT_APNS_* signing key are secrets set at deploy
-# time by deploy-push.sh via `miren deploy -e` (the .p8 lives ONLY on this app).
+# time by deploy/miren/push.sh via `miren deploy -e` (the .p8 lives ONLY on this app).