From 5c0feda8d49520a12a78790185d6d7dfe7b36b41 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 14 Jun 2026 08:19:48 +0000 Subject: [PATCH] Wire CoinPay merchant id + deploy secret injection - payments: AGENTBBS_COINPAY_MERCHANT_ID -> --business-id on the premium create/pay commands (coinpay CLI reads COINPAY_API_KEY from env for auth). - deploy.yml: forward COINPAY_API_KEY + COINPAY_MERCHANT_ID (GitHub secrets, masked in logs) to the remote setup.sh. - setup.sh: idempotent upsert_env writes those secrets into agentbbs.env (COINPAY_API_KEY, AGENTBBS_COINPAY_MERCHANT_ID, COINPAY_BUSINESS_ID), preserving the rest. No secret values are committed. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/deploy.yml | 14 ++++++++++++-- internal/payments/payments.go | 26 ++++++++++++++++++++------ setup.sh | 26 +++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bb97795..7d087bb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -51,11 +51,18 @@ jobs: # Deploy whichever branch was pushed (main or master), so a rename # "just works". For workflow_dispatch this is the chosen branch. DEPLOY_BRANCH: ${{ github.ref_name }} + # App secrets injected into the droplet's env file by setup.sh. + # GitHub masks these in logs; setup.sh upserts them idempotently. + COINPAY_API_KEY: ${{ secrets.COINPAY_API_KEY }} + COINPAY_MERCHANT_ID: ${{ secrets.COINPAY_MERCHANT_ID }} run: | ssh -i ~/.ssh/id_deploy -p "$DEPLOY_PORT" \ -o BatchMode=yes -o StrictHostKeyChecking=yes \ "${DEPLOY_USER}@${DEPLOY_HOST}" \ - "sudo -n env BRANCH=$(printf %q "$DEPLOY_BRANCH") bash -s" <<'REMOTE' + "sudo -n env BRANCH=$(printf %q "$DEPLOY_BRANCH") \ + COINPAY_API_KEY=$(printf %q "$COINPAY_API_KEY") \ + COINPAY_MERCHANT_ID=$(printf %q "$COINPAY_MERCHANT_ID") \ + bash -s" <<'REMOTE' set -euo pipefail REPO=https://github.com/profullstack/agentbbs.git BRANCH="${BRANCH:-main}" @@ -67,7 +74,10 @@ jobs: fi git -C "$SRC" fetch --depth 1 origin "$BRANCH" git -C "$SRC" reset --hard "origin/$BRANCH" - exec env BRANCH="$BRANCH" "$SRC/setup.sh" + exec env BRANCH="$BRANCH" \ + COINPAY_API_KEY="${COINPAY_API_KEY:-}" \ + COINPAY_MERCHANT_ID="${COINPAY_MERCHANT_ID:-}" \ + "$SRC/setup.sh" REMOTE - name: Smoke-test that agentbbs serves :22 diff --git a/internal/payments/payments.go b/internal/payments/payments.go index 4f5dfff..cfd9c49 100644 --- a/internal/payments/payments.go +++ b/internal/payments/payments.go @@ -40,6 +40,24 @@ func PremiumAmount() string { return envOr("AGENTBBS_PREMIUM_AMOUNT", "10") func PremiumCurrency() string { return envOr("AGENTBBS_PREMIUM_CURRENCY", "USD") } func PremiumBlockchain() string { return envOr("AGENTBBS_PREMIUM_BLOCKCHAIN", "eth") } +// MerchantID is the CoinPay merchant/business id payments are created under +// (AGENTBBS_COINPAY_MERCHANT_ID). When set it is passed as --business-id. +func MerchantID() string { return os.Getenv("AGENTBBS_COINPAY_MERCHANT_ID") } + +// premiumCreateCmd builds the default `coinpay payment create` command, adding +// --business-id when a merchant id is configured. extra is appended verbatim +// (e.g. " --json --metadata %s"). The coinpay CLI reads COINPAY_API_KEY from +// the environment for auth. +func premiumCreateCmd(extra string) string { + cmd := "coinpay payment create --amount " + PremiumAmount() + + " --currency " + PremiumCurrency() + + " --blockchain " + PremiumBlockchain() + if m := MerchantID(); m != "" { + cmd += " --business-id " + m + } + return cmd + extra +} + func envOr(k, def string) string { if v := os.Getenv(k); v != "" { return v @@ -74,9 +92,7 @@ func PremiumReference(pubkeyFP string) string { return Reference("premium", pubk func CreatePremiumCharge(ref string) (Charge, bool, error) { tmpl := os.Getenv("AGENTBBS_COINPAY_PREMIUM_CREATE_CMD") if tmpl == "" { - tmpl = "coinpay payment create --amount " + PremiumAmount() + - " --currency " + PremiumCurrency() + - " --blockchain " + PremiumBlockchain() + " --json --metadata %s" + tmpl = premiumCreateCmd(" --json --metadata %s") } line := tmpl if strings.Contains(tmpl, "%s") { @@ -120,9 +136,7 @@ func CreatePremiumCharge(ref string) (Charge, bool, error) { func PremiumPayCommand(ref string) string { tmpl := os.Getenv("AGENTBBS_COINPAY_PREMIUM_PAY_TMPL") if tmpl == "" { - tmpl = "coinpay payment create --amount " + PremiumAmount() + - " --currency " + PremiumCurrency() + - " --blockchain " + PremiumBlockchain() + " --metadata %s" + tmpl = premiumCreateCmd(" --metadata %s") } if strings.Contains(tmpl, "%s") { return fmt.Sprintf(tmpl, ref) diff --git a/setup.sh b/setup.sh index 376f225..09b3902 100755 --- a/setup.sh +++ b/setup.sh @@ -188,7 +188,11 @@ AGENTBBS_HTTP_ADDR=${HTTP_ADDR} # Premium payment via the coinpay CLI: join@ mints a charge and shows the amount # + deposit address; the status command verifies a later settlement. %s is the -# per-account payment reference. +# per-account payment reference. COINPAY_API_KEY + the merchant id are injected +# by the CI deploy from GitHub secrets (COINPAY_API_KEY / COINPAY_MERCHANT_ID); +# set them here directly when provisioning by hand: +# COINPAY_API_KEY=cp_live_xxx +# AGENTBBS_COINPAY_MERCHANT_ID= # -> --business-id # AGENTBBS_PREMIUM_AMOUNT=10 # AGENTBBS_PREMIUM_CURRENCY=USD # AGENTBBS_PREMIUM_BLOCKCHAIN=eth @@ -213,6 +217,26 @@ ENV chmod 0640 "$ENV_DIR/agentbbs.env" fi +# Idempotently upsert secrets passed in the environment (e.g. by the CI deploy +# from GitHub Actions secrets) into agentbbs.env, preserving everything else. +# Secrets are never committed — they live only here and in encrypted CI storage. +upsert_env() { # KEY VALUE — skips when VALUE is empty + local key="$1" val="$2" file="$ENV_DIR/agentbbs.env" + [ -n "$val" ] || return 0 + touch "$file" + if grep -qE "^${key}=" "$file"; then + # Replace in place (| delimiter avoids clashes with / or & in the value). + sed -i "s|^${key}=.*|${key}=${val}|" "$file" + else + printf '%s=%s\n' "$key" "$val" >> "$file" + fi + chmod 0640 "$file" +} +# CoinPay: API key (read by the coinpay CLI) + merchant/business id. +upsert_env COINPAY_API_KEY "${COINPAY_API_KEY:-}" +upsert_env AGENTBBS_COINPAY_MERCHANT_ID "${COINPAY_MERCHANT_ID:-${AGENTBBS_COINPAY_MERCHANT_ID:-}}" +upsert_env COINPAY_BUSINESS_ID "${COINPAY_MERCHANT_ID:-${AGENTBBS_COINPAY_MERCHANT_ID:-}}" + # ---- 7. systemd unit (runs as $SVC_USER, binds :22 via ambient capability) -- log "installing agentbbs.service" cat > /etc/systemd/system/agentbbs.service <