Skip to content

Free AI Chat, Tools & Fun — Cloudflare edge gateway

No login. Streaming chat. Internal tools and image fun pages.

VibeWatcher
Go back

Secure Token Delivery from a Public Repo: GitHub Actions + age-encrypted Releases + VPS Auto-Health & Cleanup

Published:

If you generate tokens frequently (sometimes dozens of runs per day) and your repository must remain public, “just zip it and send it somewhere” quickly becomes a security and operations mess.

This post describes a practical end-to-end pipeline that keeps secrets safe while staying highly automated:

Token factory → encrypted publishing → VPS sync → health probing → purge invalid tokens → Telegram report.

All sensitive values are removed; anything secret is shown as placeholders.

The core idea: publish ciphertext artifacts, never plaintext

Treat tokens as build artifacts:

This is why a public repo is still OK: the Release only contains ciphertext.

Part A — GitHub Actions: generate → zip → encrypt → publish

What you configure

In GitHub Actions Secrets (or Variables), provide:

In the workflow, keep:

permissions:
  contents: write

So ${{ github.token }} can create Releases and upload assets without an extra PAT.

Tag strategy: make every run unique

If you run often, avoid tokens-YYYYMMDD tags. Use something like:

tokens-YYYYMMDD-HHMMSS-r<RUN_NUMBER>-a<RUN_ATTEMPT>

Example workflow

name: Daily Task Job

on:
  schedule:
    - cron: "*/40 * * * *"
  workflow_dispatch:
    inputs:
      count:
        description: "How many accounts to register"
        required: false
        default: "40"

permissions:
  contents: write
  actions: read

jobs:
  register:
    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: "pip"

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run registration
        env:
          REG_COUNT: ${{ github.event.inputs.count || '40' }}
        run: |
          mkdir -p codex
          for i in $(seq 1 $REG_COUNT); do
            python task_runner.py --once || true
            sleep $((RANDOM % 20 + 10))
          done

      - name: Install age
        if: always()
        run: |
          sudo apt-get update
          sudo apt-get install -y age

      - name: Package + Encrypt + Upload Release asset
        if: always()
        env:
          AGE_RECIPIENT: ${{ secrets.AGE_RECIPIENT }}
          GH_TOKEN: ${{ github.token }}
        run: |
          set -euo pipefail

          COUNT=$(( $(find codex -name '*.json' 2>/dev/null | wc -l) + 0 ))
          [ "$COUNT" -gt 0 ] || exit 0

          ZIP_NAME="tokens.zip"
          ENC_NAME="tokens.zip.age"
          MANIFEST="manifest.json"

          rm -f "$ZIP_NAME" "$ENC_NAME" "$MANIFEST"
          zip -r "$ZIP_NAME" codex/*.json

          age -r "$AGE_RECIPIENT" -o "$ENC_NAME" "$ZIP_NAME"

          SHA256=$(sha256sum "$ENC_NAME" | awk '{print $1}')
          DATE_TAG=$(date +%Y%m%d-%H%M%S)
          TAG="tokens-${DATE_TAG}-r${GITHUB_RUN_NUMBER}-a${GITHUB_RUN_ATTEMPT}"

          cat > "$MANIFEST" <<EOF
          {
            "tag": "${TAG}",
            "sha256": "${SHA256}",
            "count": ${COUNT},
            "generated_at": "$(date -Is)"
          }
          EOF

          gh release create "$TAG" \
            --title "Tokens ${DATE_TAG}" \
            --notes "Encrypted token bundle (assets are age-encrypted)." \
            --latest=false || true

          gh release upload "$TAG" "$ENC_NAME" "$MANIFEST" --clobber

Part B — Release cleanup: keep only the last 6 hours

High-frequency publishing needs aggressive cleanup. Run this every 15 minutes and delete tokens-* releases older than 6 hours:

name: Cleanup old token releases (keep 6 hours)

on:
  schedule:
    - cron: "*/15 * * * *"
  workflow_dispatch:

permissions:
  contents: write

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Delete token releases older than 6 hours
        env:
          GH_TOKEN: ${{ github.token }}
          REPO: ${{ github.repository }}
          KEEP_HOURS: 6
        run: |
          set -euo pipefail

          cutoff_epoch=$(date -u -d "${KEEP_HOURS} hours ago" +%s)

          gh api "repos/${REPO}/releases?per_page=100" --paginate |
            jq -r '.[] | [.tag_name, .created_at] | @tsv' |
          while IFS=$'\t' read -r tag created_at; do
            case "$tag" in
              tokens-*)
                created_epoch=$(date -u -d "$created_at" +%s)
                if [ "$created_epoch" -lt "$cutoff_epoch" ]; then
                  gh release delete "$tag" -y
                fi
                ;;
            esac
          done

Part C — VPS sync: download → verify → decrypt → append

A VPS syncer should:

Using published_at for incremental sync tends to be more robust than created_at under high-frequency releases.

Part D — Full health probe + purge invalid tokens

After syncing, you still need to know which tokens are usable. The most reliable approach is a real request probe, for example:

GET https://chatgpt.com/backend-api/wham/usage

Then classify by HTTP status:

Throttle and set timeouts to avoid upstream rate-limits.

Part E — Telegram report (multi-line, no truncation)

Telegram is for reporting stats, not distributing secrets.

To avoid “only first line arrives” issues:

#GitHubActions #age #Systemd #DevOps #Telegram #Security #Tokens


Share this post on:

Previous Post
Don’t Force PVE onto Oracle ARM: How I Split One Host into Two Maintainable Mini VPSes with Incus Routed
Next Post
OpenClaw Memory Archiving, Compression, Reset, and Recovery Workflow
🎵