deploy: release-server.sh pulls the signing key from Bitwarden automatically (#1192)
Bitwarden is the standing store for operator secrets now, so publish/promote shouldn't require hand-exporting LCHAT_RELEASE_KEY. When no key is provided any other way and the `bw` CLI is present, the script pulls the signing seed from the "Eleven Messenger server release signing key" item itself — unlocking interactively if the vault is locked. The seed stays in the env only, never on disk (same property as the explicit flow). Explicitly setting LCHAT_RELEASE_KEY / _KEY_FILE / - still wins and bypasses bw. Applies to both publish and promote (both re-sign the feed, so both need the key) — so `promote <ver> stable` now just works with bw installed, no manual key setup.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+41
−13
deploy/release-server.sh+41 −13
diff --git a/deploy/release-server.sh b/deploy/release-server.sh| index 8158e95..bd51616 100755 |
| --- a/deploy/release-server.sh |
| +++ b/deploy/release-server.sh |
| @@ -22,22 +22,24 @@ |
| # in CLAUDE.local.md, never here). `publish ... && ship` is a full deploy. |
| # |
| # Needs: gh (authed, write access to $LCHAT_RELEASES_REPO), jq, go, and the |
| -# signing key. Preferred: the seed VALUE in $LCHAT_RELEASE_KEY — it goes |
| -# secret manager -> shell -> the signing tool's memory and never touches disk |
| -# (one fetch covers a whole publish && promote chain): |
| +# signing key. Both publish AND promote re-sign the feed, so both need the key. |
| # |
| -# export LCHAT_RELEASE_KEY="$(bw get item "Eleven Messenger server release signing key" \ |
| -# | jq -r '.fields[]? | select((.value|length)>40).value' | head -1)" |
| -# ./deploy/release-server.sh publish "notes" |
| +# The easy path — nothing to set up: with the Bitwarden CLI (`bw`) installed, |
| +# just run the command and it pulls the seed from Bitwarden itself (unlocking |
| +# interactively if the vault is locked). One unlock covers a publish && promote |
| +# chain; the seed stays in the env only, never on disk. |
| # |
| -# (The Bitwarden item has no password field — the seed rides a hidden custom |
| -# field, which that jq picks out by being the only long value.) |
| +# ./deploy/release-server.sh publish "notes" |
| +# ./deploy/release-server.sh promote <ver> stable |
| # |
| -# Fallbacks: $LCHAT_RELEASE_KEY_FILE as a file path, or `-` to read the key |
| -# from stdin (parked in this run's tmpdir, wiped on exit). |
| +# (The Bitwarden item — "Eleven Messenger server release signing key" — has no |
| +# password field; the seed rides a hidden custom field, picked out as the only |
| +# >40-char value. Legacy copy in 1Password.) |
| # |
| -# (The key lives in Bitwarden — legacy copy in 1Password — as "Eleven |
| -# Messenger server release signing key".) |
| +# Override the auto-pull by providing the key yourself: the seed VALUE in |
| +# $LCHAT_RELEASE_KEY (env, never touches disk), $LCHAT_RELEASE_KEY_FILE as a |
| +# file path, or `-` to read it from stdin (parked in this run's tmpdir, wiped |
| +# on exit). Any of these set → Bitwarden is not consulted. |
| # |
| # Optional S3 mirror: set LCHAT_RELEASE_MIRROR_BUCKET (needs the aws CLI) and |
| # every feed artifact is also copied to s3://<bucket>/releases/… — GitHub's |
| @@ -67,6 +69,32 @@ trap 'rm -rf "$TMP"' EXIT |
| # and the gh/jq toolchain are required only where the feed is touched |
| # (publish/promote, checked below). |
| case "${1:-}" in publish|promote) |
| + # No key provided any other way? If the Bitwarden CLI is present, pull the |
| + # signing seed from there — bw is the standing store for operator secrets, so |
| + # neither publish NOR promote should need a hand-exported key (both re-sign the |
| + # feed, so both need it). Unlocks interactively if the vault is locked; the |
| + # seed lands in the env only, never on disk (same property as the explicit |
| + # LCHAT_RELEASE_KEY flow). No bw and no key set → the original error below. |
| + if [ -z "${LCHAT_RELEASE_KEY:-}" ] && [ -z "$KEY" ] \ |
| + && command -v bw >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then |
| + # The item has no password field — the seed rides a hidden custom field, |
| + # picked out as the only >40-char value (matches the header's incantation). |
| + bw_key() { bw get item "Eleven Messenger server release signing key" 2>/dev/null \ |
| + | jq -r '.fields[]? | select((.value|length)>40).value' | head -1; } |
| + LCHAT_RELEASE_KEY="$(bw_key)" |
| + if [ -z "$LCHAT_RELEASE_KEY" ]; then |
| + # Empty usually means the vault is locked (or this shell has no session): |
| + # unlock — prompts for the master password on the tty — then read again. |
| + echo "release-server: unlocking Bitwarden to read the signing key…" >&2 |
| + BW_SESSION="$(bw unlock --raw)" && export BW_SESSION || { |
| + echo "release-server: Bitwarden unlock failed — unlock it (or set LCHAT_RELEASE_KEY), then retry" >&2; exit 1; } |
| + LCHAT_RELEASE_KEY="$(bw_key)" |
| + fi |
| + [ -n "$LCHAT_RELEASE_KEY" ] || { |
| + echo "release-server: couldn't read 'Eleven Messenger server release signing key' from Bitwarden" >&2; exit 1; } |
| + export LCHAT_RELEASE_KEY # the signing tool (a child process) reads it from the env |
| + echo "release-server: signing key sourced from Bitwarden" >&2 |
| + fi |
| if [ -n "${LCHAT_RELEASE_KEY:-}" ]; then |
| # The seed itself is in the environment — the preferred flow: it goes |
| # secret manager -> shell -> the signing tool's memory and never touches |
| @@ -81,7 +109,7 @@ case "${1:-}" in publish|promote) |
| KEY="$TMP/signing.key" |
| else |
| [ -n "$KEY" ] && [ -f "$KEY" ] || { |
| - echo "release-server: set LCHAT_RELEASE_KEY to the base64 seed (or LCHAT_RELEASE_KEY_FILE to a key file, or - for stdin)" >&2; exit 1; } |
| + echo "release-server: set LCHAT_RELEASE_KEY to the base64 seed — or install the Bitwarden CLI 'bw' to pull it automatically (LCHAT_RELEASE_KEY_FILE to a key file, or - for stdin, also work)" >&2; exit 1; } |
| fi |
| command -v jq >/dev/null || { echo "release-server: jq is required" >&2; exit 1; } |
| command -v gh >/dev/null || { echo "release-server: gh is required" >&2; exit 1; } |