deploy: release-server accepts the signing key on stdin (LCHAT_RELEASE_KEY_FILE=-) (#1100)
A release no longer needs a hand-managed key file: a secret manager can pipe the key straight in, it lands only in the run's private tmpdir and the existing trap wipes it. ship is untouched (the case never reads stdin).
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+23
−7
deploy/release-server.sh+23 −7
diff --git a/deploy/release-server.sh b/deploy/release-server.sh| index eadeb26..6ea0e2d 100755 |
| --- a/deploy/release-server.sh |
| +++ b/deploy/release-server.sh |
| @@ -22,8 +22,15 @@ |
| # 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 at $LCHAT_RELEASE_KEY_FILE (1Password: "Eleven Messenger server |
| -# release signing key"). |
| +# signing key at $LCHAT_RELEASE_KEY_FILE — a file path, or `-` to read the key |
| +# from stdin, so a secret manager pipes it straight in and nothing is left on |
| +# disk or in the environment: |
| +# |
| +# <fetch key from Bitwarden/1Password> \ |
| +# | LCHAT_RELEASE_KEY_FILE=- ./deploy/release-server.sh publish "notes" |
| +# |
| +# (The key lives in Bitwarden and 1Password as "Eleven Messenger server |
| +# release signing key".) |
| # |
| # 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 |
| @@ -41,12 +48,24 @@ MIRROR_BUCKET="${LCHAT_RELEASE_MIRROR_BUCKET:-}" |
| BIN=lchat-linux-amd64 |
| BIN_ARM=lchat-linux-arm64 |
| +TMP="$(mktemp -d)" |
| +trap 'rm -rf "$TMP"' EXIT |
| + |
| # ship only drives the box over ssh — no signing, no feed writes — so the key |
| # and the gh/jq toolchain are required only where the feed is touched |
| # (publish/promote, checked below). |
| case "${1:-}" in publish|promote) |
| - [ -n "$KEY" ] && [ -f "$KEY" ] || { |
| - echo "release-server: set LCHAT_RELEASE_KEY_FILE to the signing key file" >&2; exit 1; } |
| + if [ "$KEY" = "-" ]; then |
| + # Key arrives on stdin: park it in this run's private tmpdir (0700 from |
| + # mktemp -d, wiped by the trap) so nothing persists past the run. |
| + cat > "$TMP/signing.key" |
| + [ -s "$TMP/signing.key" ] || { |
| + echo "release-server: LCHAT_RELEASE_KEY_FILE=- but nothing arrived on stdin" >&2; exit 1; } |
| + KEY="$TMP/signing.key" |
| + else |
| + [ -n "$KEY" ] && [ -f "$KEY" ] || { |
| + echo "release-server: set LCHAT_RELEASE_KEY_FILE to the signing key file (or - to read it from stdin)" >&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; } |
| [ -z "$MIRROR_BUCKET" ] || command -v aws >/dev/null || { |
| @@ -54,9 +73,6 @@ case "${1:-}" in publish|promote) |
| ;; |
| esac |
| -TMP="$(mktemp -d)" |
| -trap 'rm -rf "$TMP"' EXIT |
| - |
| # The signing/manifest code lives in the binary itself, so builder and box |
| # can't drift on the format. Build a local tool from this checkout (only the |
| # feed-touching commands need it). |