Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions .github/workflows/create-empty-local-db-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ name: Create Empty Local DB Manifest
env:
SPACES_ACCESS_KEY: ${{ secrets.SPACES_ACCESS_KEY }}
SPACES_SECRET_KEY: ${{ secrets.SPACES_SECRET_KEY }}
SPACES_REGION: ${{ secrets.SPACES_REGION }}
SPACES_BUCKET: ${{ secrets.SPACES_BUCKET }}
SPACES_ENDPOINT: ${{ secrets.SPACES_ENDPOINT }}

on:
workflow_dispatch:
Expand All @@ -30,12 +27,31 @@ jobs:
keep-env-derivations = true
keep-outputs = true

- name: Load encrypted runtime config
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
# shellcheck disable=SC1091
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
Comment on lines +30 to +47
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider cleaning up the decrypted secrets file.

The decrypted runtime config at /tmp/local-db-runtime.env contains sensitive values and persists after the step completes. Consider adding cleanup for defense in depth.

🛡️ Suggested cleanup
          {
            echo "DUMP_BASE_URL=$DUMP_BASE_URL"
            echo "SPACES_REGION=$SPACES_REGION"
            echo "SPACES_BUCKET=$SPACES_BUCKET"
            echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
          } >> "$GITHUB_ENV"
+         rm -f /tmp/local-db-runtime.env
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Load encrypted runtime config
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
# shellcheck disable=SC1091
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
- name: Load encrypted runtime config
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
# shellcheck disable=SC1091
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
rm -f /tmp/local-db-runtime.env
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/create-empty-local-db-manifest.yaml around lines 30 - 47,
The decrypted runtime file /tmp/local-db-runtime.env created in the "Load
encrypted runtime config" step contains sensitive secrets and must be removed
after use; modify the step so that after sourcing and appending the needed
variables to GITHUB_ENV you securely delete the temporary file (e.g.,
overwrite/shred if available or at minimum rm -f /tmp/local-db-runtime.env) and
ensure it had restrictive permissions while present; reference the commands that
create/source the file (nix shell ... > /tmp/local-db-runtime.env and source
/tmp/local-db-runtime.env) and add a cleanup action immediately after exporting
the variables.


Comment on lines +30 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider extracting the runtime config loading to a reusable composite action.

This decryption pattern (SSH key setup + rage decrypt + export to $GITHUB_ENV) is duplicated across deploy.yaml and this workflow. A composite action would reduce maintenance burden and ensure consistency.

♻️ Example composite action structure

Create .github/actions/load-runtime-config/action.yaml:

name: Load encrypted runtime config
description: Decrypt and load runtime.env.age into GITHUB_ENV

inputs:
  ssh_key:
    description: SSH private key for decryption
    required: true

runs:
  using: composite
  steps:
    - shell: bash
      run: |
        set -euo pipefail
        mkdir -p ~/.ssh
        printf '%s\n' "${{ inputs.ssh_key }}" > ~/.ssh/id_ed25519
        chmod 600 ~/.ssh/id_ed25519
        nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
        set -a
        source /tmp/local-db-runtime.env
        set +a
        {
          echo "DUMP_BASE_URL=$DUMP_BASE_URL"
          echo "SPACES_REGION=$SPACES_REGION"
          echo "SPACES_BUCKET=$SPACES_BUCKET"
          echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
        } >> "$GITHUB_ENV"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/create-empty-local-db-manifest.yaml around lines 30 - 48,
Extract the repeated SSH/rage decryption block into a reusable composite action
(e.g. create .github/actions/load-runtime-config/action.yaml) that exposes an
input like "ssh_key" and implements the steps currently in the workflow (mkdir
~/.ssh, write and chmod id_ed25519, run rage to decrypt config/runtime.env.age
to /tmp/local-db-runtime.env, source it and append DUMP_BASE_URL, SPACES_REGION,
SPACES_BUCKET, SPACES_ENDPOINT to $GITHUB_ENV); then replace the inline block in
this workflow and deploy.yaml with a single step that calls the composite action
and passes secrets.SSH_KEY as the ssh_key input.

- name: Create and upload empty manifest
id: create_manifest
shell: bash
run: |
set -euo pipefail
manifest_url="$(nix run .#local-db-create-empty-manifest)"
manifest_url="$(nix run .#local-db-create-empty-manifest | tail -n 1)"
echo "manifest_url=$manifest_url" >>"$GITHUB_OUTPUT"

- name: Summarize manifest URL
Expand Down
57 changes: 51 additions & 6 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ name: Deploy
on:
workflow_dispatch:
inputs:
target_ref:
description: Git ref to deploy from
required: true
default: main
type: string
allow_non_main:
description: Allow deployment from a non-main ref
required: true
default: false
type: boolean
settings_url:
description: Settings YAML URL to deploy
required: true
Expand All @@ -18,22 +28,31 @@ permissions:

env:
HYPER_RPC_API_TOKEN: ${{ secrets.HYPER_RPC_API_TOKEN }}
DUMP_BASE_URL: ${{ secrets.DUMP_BASE_URL }}
PUBLIC_WALLETCONNECT_PROJECT_ID: "00000000000000000000000000000000"
SPACES_ACCESS_KEY: ${{ secrets.SPACES_ACCESS_KEY }}
SPACES_SECRET_KEY: ${{ secrets.SPACES_SECRET_KEY }}
SPACES_REGION: ${{ secrets.SPACES_REGION }}
SPACES_BUCKET: ${{ secrets.SPACES_BUCKET }}
SPACES_ENDPOINT: ${{ secrets.SPACES_ENDPOINT }}
TAILSCALE_HOSTNAME: local-db-remote
TARGET_REF: ${{ inputs.target_ref }}

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Validate target ref
shell: bash
run: |
set -euo pipefail
if [ "$TARGET_REF" != "main" ] && [ "${{ inputs.allow_non_main }}" != "true" ]; then
echo "Set allow_non_main=true to deploy from a non-main ref." >&2
exit 1
fi

- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1

- uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_REF }}
submodules: recursive
fetch-depth: 0

Expand All @@ -54,17 +73,40 @@ jobs:
printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519

- name: Load encrypted runtime config
shell: bash
run: |
set -euo pipefail
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
# shellcheck disable=SC1091
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
Comment on lines +76 to +90
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider cleaning up the decrypted secrets file.

Same as in create-empty-local-db-manifest.yaml: the decrypted runtime config at /tmp/local-db-runtime.env persists after the step. Consider adding rm -f /tmp/local-db-runtime.env at the end for defense in depth.

🛡️ Suggested cleanup
          {
            echo "DUMP_BASE_URL=$DUMP_BASE_URL"
            echo "SPACES_REGION=$SPACES_REGION"
            echo "SPACES_BUCKET=$SPACES_BUCKET"
            echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
          } >> "$GITHUB_ENV"
+         rm -f /tmp/local-db-runtime.env
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Load encrypted runtime config
shell: bash
run: |
set -euo pipefail
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
# shellcheck disable=SC1091
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
- name: Load encrypted runtime config
shell: bash
run: |
set -euo pipefail
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
# shellcheck disable=SC1091
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
rm -f /tmp/local-db-runtime.env
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy.yaml around lines 74 - 88, The decrypted runtime
file /tmp/local-db-runtime.env is left on disk after the "Load encrypted runtime
config" step; update that step (the shell block that runs nix/rage and sources
/tmp/local-db-runtime.env) to securely remove the file when done by adding a
removal command (e.g., rm -f /tmp/local-db-runtime.env) after exporting the
variables to $GITHUB_ENV so the temporary secret file does not persist on the
runner.


- name: Connect runner to Tailscale
uses: tailscale/github-action@v4
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:ci

- name: Prepare raindex submodule
run: ./prep.sh

- name: Build local CLI artifact
run: nix run .#build-raindex-cli

- name: Resolve host IP
- name: Resolve host over Tailscale
shell: bash
run: |
set -euo pipefail
host_ip="$(nix run .#resolve-ip)"
host_ip="$(tailscale ip -4 "$TAILSCALE_HOSTNAME")"
echo "::add-mask::$host_ip"
echo "HOST_IP=$host_ip" >> "$GITHUB_ENV"

Expand All @@ -75,6 +117,8 @@ jobs:
ssh-keyscan -H "$HOST_IP" >> ~/.ssh/known_hosts

- name: Deploy NixOS configuration
env:
DEPLOY_HOST: ${{ env.HOST_IP }}
run: nix run .#deploy-nixos

- name: Upload runtime assets and restart service
Expand Down Expand Up @@ -124,6 +168,7 @@ jobs:
{
echo "## Local DB Remote Deployment"
echo
echo "- Target ref: $TARGET_REF"
echo "- Host IP: $HOST_IP"
echo "- Settings URL: ${{ inputs.settings_url }}"
echo "- Timer: local-db-sync.timer"
Expand Down
22 changes: 17 additions & 5 deletions .github/workflows/provision-host.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ name: Provision Host
on:
workflow_dispatch:
inputs:
target_ref:
description: Git ref to provision from
required: true
default: main
type: string
allow_non_main:
description: Allow provisioning from a non-main ref
required: true
default: false
type: boolean
bootstrap_nixos:
description: Bootstrap NixOS onto the provisioned droplet
required: true
Expand All @@ -22,13 +32,14 @@ jobs:
runs-on: ubuntu-latest
env:
TF_VAR_do_token: ${{ secrets.DO_TOKEN }}
TARGET_REF: ${{ inputs.target_ref }}
steps:
- name: Require main branch
- name: Validate target ref
shell: bash
run: |
set -euo pipefail
if [ "${GITHUB_REF_NAME}" != "main" ]; then
echo "This workflow must be run from the main branch." >&2
if [ "$TARGET_REF" != "main" ] && [ "${{ inputs.allow_non_main }}" != "true" ]; then
echo "Set allow_non_main=true to provision from a non-main ref." >&2
exit 1
fi
Comment on lines +37 to 44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Constrain target_ref to an existing branch before state push.

Line 90 assumes TARGET_REF is a branch destination. Current validation (Line 37-Line 44) permits tag/SHA-style refs when allow_non_main=true, which can let provisioning run and then fail persisting encrypted Terraform state.

🔧 Suggested hardening
       - uses: actions/checkout@v4
         with:
           ref: ${{ env.TARGET_REF }}
           submodules: recursive
           fetch-depth: 0
+
+      - name: Ensure target_ref is an existing remote branch
+        shell: bash
+        run: |
+          set -euo pipefail
+          if ! git ls-remote --exit-code --heads origin "$TARGET_REF" >/dev/null; then
+            echo "target_ref must be an existing branch name because terraform state is pushed back to it." >&2
+            exit 1
+          fi

Also applies to: 90-90

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/provision-host.yaml around lines 37 - 44, The current
Validate target ref step allows tag/SHA refs when allow_non_main=true, which can
lead to attempting a state push to a non-branch ref; update that step to
explicitly verify TARGET_REF is an existing branch before proceeding (e.g., run
a git query such as git ls-remote --heads origin "$TARGET_REF" or use the GitHub
API to confirm a refs/heads/$TARGET_REF exists) and fail with a clear error if
the branch is missing; keep the existing allow_non_main gating but add this
branch-existence check in the same step so the later state push only runs for
valid branch destinations.


Expand All @@ -37,7 +48,7 @@ jobs:

- uses: actions/checkout@v4
with:
ref: main
ref: ${{ env.TARGET_REF }}
submodules: recursive
fetch-depth: 0

Expand Down Expand Up @@ -76,7 +87,7 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add infra/terraform.tfstate.age infra/terraform.tfvars.age
git commit -m "chore: update local-db remote terraform state"
git push origin HEAD:main
git push origin HEAD:"$TARGET_REF"

- name: Resolve host IP
id: resolve_ip
Expand All @@ -100,6 +111,7 @@ jobs:
{
echo "## Host Provisioning"
echo
echo "- Target ref: $TARGET_REF"
echo "- Host IP: ${{ steps.resolve_ip.outputs.host_ip }}"
echo "- Bootstrap NixOS: ${{ inputs.bootstrap_nixos }}"
echo "- Terraform apply ran successfully"
Expand Down
Binary file added config/runtime.env.age
Binary file not shown.
4 changes: 4 additions & 0 deletions config/runtime.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DUMP_BASE_URL=
SPACES_REGION=
SPACES_BUCKET=
SPACES_ENDPOINT=
3 changes: 3 additions & 0 deletions config/secrets.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"runtime.env.age".publicKeys = (import ../keys.nix).roles.infra;
}
9 changes: 7 additions & 2 deletions deploy.nix
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ in {
infraPkgs.buildInputs ++ [ deploy-rs.packages.${localSystem}.deploy-rs ];

deployPreamble = ''
${infraPkgs.resolveIp}
export DEPLOY_HOST="$host_ip"
${infraPkgs.parseIdentity}
if [ -n "''${DEPLOY_HOST:-}" ]; then
host_ip="$DEPLOY_HOST"
else
${infraPkgs.resolveIp}
export DEPLOY_HOST="$host_ip"
fi
export NIX_SSHOPTS="-i $identity"
ssh_flag="--ssh-opts=-i $identity"
'';
Expand Down
17 changes: 17 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading