Skip to content
Merged
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
59 changes: 58 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ The document will go over all the Github Actions that currently automate a coupl
* [Push templates to review firm (push_to_review_firm.yml)](#push-templates-to-review-firm-push_to_review_firmyml)
* [Liquid sampler](#liquid-sampler)
* [Run liquid sampler (run_sampler.yml)](#run-liquid-sampler-run_sampleryml)
* [Authorizing a partner for staging (scripts/authorize-partner-secret.sh)](#authorizing-a-partner-for-staging-scriptsauthorize-partner-secretsh)


## Overview
Expand Down Expand Up @@ -284,4 +285,60 @@ _Prerequisites:_

* `SF_API_CLIENT_ID`, `SF_API_SECRET`, `PARTNER_CONFIG_JSON` (per-partner secret resolved by the caller) and `REPO_ACCESS_TOKEN` (repo-scoped write PAT, for the token write-back) available to the caller.
* `SF_BASIC_AUTH` if the partner's host is a `*.staging.getsilverfin.com` gateway.
* The partner must already be authorized with the Silverfin CLI (`silverfin authorize-partner`) and its `config.json` stored as the `PARTNER_CONFIG_JSON_<partner>` secret.
* The partner must already be authorized with the Silverfin CLI (`silverfin authorize-partner`) and its `config.json` stored as the `PARTNER_CONFIG_JSON_<partner>` secret.

#### Authorizing a partner for staging (`scripts/authorize-partner-secret.sh`)

_Description:_
One-off setup script that authorizes a partner `api_key` against staging and stores the result as that partner's `PARTNER_CONFIG_JSON_<partner_id>` GitHub secret β€” the secret [`run_sampler.yml`](#run-liquid-sampler-run_sampleryml) reads from. Run this once per partner, and again any time a partner's staging token is lost for good (see _When you need to re-run this_ below) β€” day-to-day token rotation during sampler runs is handled automatically by the workflow itself and does **not** need this script.
Comment on lines +288 to +293

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

πŸ—„οΈ Data Integrity & Integration | 🟑 Minor | ⚑ Quick win

Clarify the bootstrap exception to the β€œsole writer” contract.

The README says the workflow is the sole writer, while this section says the script uploads the initial secret. State explicitly that this script bootstraps credentials and run_sampler.yml owns subsequent token-rotation writes.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 288 - 293, Clarify the credential ownership contract
in the README section for scripts/authorize-partner-secret.sh: state that this
script only bootstraps the initial PARTNER_CONFIG_JSON_<partner_id> secret,
while run_sampler.yml is the sole writer for all subsequent token-rotation
updates.


_Prerequisites:_

* `silverfin` CLI installed and on `PATH`.
* `gh` CLI installed and authenticated (`gh auth login`), with write access to the target market repo's secrets.
* A fresh partner `api_key`, obtained via the staging login flow below. Get this **right before** running the script β€” the token is shown once, in a banner, and you paste it straight into the prompt.

_Getting the partner `api_key` (staging login flow):_

By default your browser is logged into production (`https://live.getsilverfin.com`). To fetch a staging partner token:

1. In your browser, change the URL from `https://live.getsilverfin.com` to `https://bso-staging-beta.staging.getsilverfin.com`.
2. A basic-auth popup appears β€” log in with the **"Silverfin staging basic-auth"** credentials from 1Password. (This is the staging gateway's basic auth, not your Silverfin login.)
3. Adjust the URL again to `https://bso-staging-beta.staging.getsilverfin.com/partners`.
4. Log in with your normal partner credentials for the specific partner id you want to authorize.
5. In that partner env, click **"Configuration partners"** in the header.
6. Click the red **"Refresh API token"** button.
7. A banner appears with the token β€” copy it. Then immediately run the script (below) and paste the token when it prompts for the api key.
8. **Repeat steps 3–7 for every partner env** you need to authorize β€” each partner has its own login and its own token.

_Usage:_

```bash
./scripts/authorize-partner-secret.sh <partner_id> <market> [host]
```

* `<partner_id>` β€” numeric partner environment id, e.g. `2`.
* `<market>` β€” either a short code (`nl`, `be`, `lu`, `uk`, mapped to `silverfin/<market>_market` in the script's `MARKET_REPOS` table) or a full `owner/repo`.
* `[host]` β€” optional. Defaults to `https://bso-staging-beta.staging.getsilverfin.com` (the same staging beta host used in the login flow above). Pass an explicit URL to target a different environment, or edit the `HOST` default in the script if you're permanently moving to a new environment.

Examples:

```bash
./scripts/authorize-partner-secret.sh 2 nl
./scripts/authorize-partner-secret.sh 11 lu
./scripts/authorize-partner-secret.sh 1 silverfin/be_market
./scripts/authorize-partner-secret.sh 2 nl https://some-other-staging-host.example.com
```

_What it does:_

* Prompts for the api key (hidden input).
* Creates a throwaway `HOME` directory so the authorization doesn't touch your real `~/.silverfin/config.json`.
* Runs `silverfin config --set-host <host>` and `silverfin authorize-partner -i <partner_id> -k <api_key> -n partner-<partner_id>` inside that throwaway `HOME`.
* Pushes the resulting `config.json` straight to the `PARTNER_CONFIG_JSON_<partner_id>` secret on the target repo via `gh secret set`.
* Deletes the throwaway `HOME` on exit (success or failure), via a `trap`.

_When you need to re-run this:_

* **Not needed for normal 401s.** A partner token that's simply gone stale (>24h old) self-heals β€” the sampler workflow's own refresh call authenticates on a digest match, not the token's age, so it mints a fresh token automatically mid-run and writes it back itself.
* **Re-run this script only after a staging DB snapshot/reset**, which overwrites the partner's stored credentials server-side β€” that invalidates any token you're holding client-side, including the refresh path, so the sampler workflow's automatic 401 recovery also fails. Signature: a partner token fails on first use *and* on the workflow's automatic refresh attempt in the same run. Get a backend/console regeneration of the partner's api_key first, then re-run this script with the new key.
50 changes: 50 additions & 0 deletions scripts/authorize-partner-secret.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Authorize a Silverfin partner api_key against an isolated throwaway HOME,
# then push the resulting config.json straight to a market repo's
# PARTNER_CONFIG_JSON_<partner_id> GitHub secret. Cleans up after itself.
#
# Usage:
# ./authorize-partner-secret.sh <partner_id> <market> [host]
#
# <market> is either a short code (nl, be, lu, uk) or a full "owner/repo".
#
# Examples:
# ./authorize-partner-secret.sh 2 nl
# ./authorize-partner-secret.sh 11 lu
# ./authorize-partner-secret.sh 1 silverfin/be_market
#
# Prompts for the api_key with hidden input (never as an argv, so it never
# lands in shell history). Re-run once per partner after each staging reset.

set -euo pipefail

declare -A MARKET_REPOS=(
[nl]="silverfin/nl_market"
[be]="silverfin/be_market"
[lu]="silverfin/lu_market"
[uk]="silverfin/uk_market"
)

PARTNER_ID="${1:?Usage: $0 <partner_id> <market> [host]}"
MARKET="${2:?Usage: $0 <partner_id> <market> [host]}"
HOST="${3:-https://bso-staging-beta.staging.getsilverfin.com}"
Comment on lines +28 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟑 Minor | ⚑ Quick win

Reject non-numeric partner IDs before authorization.

The README defines partner_id as numeric, but arbitrary values currently flow into the Silverfin command and GitHub secret name. Validate the input before prompting for the API key.

Proposed fix
 PARTNER_ID="${1:?Usage: $0 <partner_id> <market> [host]}"
 MARKET="${2:?Usage: $0 <partner_id> <market> [host]}"
 HOST="${3:-https://bso-staging-beta.staging.getsilverfin.com}"
+
+if [[ ! "$PARTNER_ID" =~ ^[0-9]+$ ]]; then
+  printf '%s\n' "partner_id must be numeric" >&2
+  exit 2
+fi
πŸ“ 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
PARTNER_ID="${1:?Usage: $0 <partner_id> <market> [host]}"
MARKET="${2:?Usage: $0 <partner_id> <market> [host]}"
HOST="${3:-https://bso-staging-beta.staging.getsilverfin.com}"
PARTNER_ID="${1:?Usage: $0 <partner_id> <market> [host]}"
MARKET="${2:?Usage: $0 <partner_id> <market> [host]}"
HOST="${3:-https://bso-staging-beta.staging.getsilverfin.com}"
if [[ ! "$PARTNER_ID" =~ ^[0-9]+$ ]]; then
printf '%s\n' "partner_id must be numeric" >&2
exit 2
fi
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/authorize-partner-secret.sh` around lines 28 - 30, In
scripts/authorize-partner-secret.sh, validate PARTNER_ID immediately after its
assignment and before any API-key prompt or authorization command, rejecting
values that are not numeric with a clear usage/error message. Preserve the
existing MARKET and HOST argument handling and continue only for valid partner
IDs.


if [[ -n "${MARKET_REPOS[$MARKET]:-}" ]]; then
REPO="${MARKET_REPOS[$MARKET]}"
else
REPO="$MARKET"
fi

printf '%s' "API key for partner ${PARTNER_ID} (${REPO}): "
read -rs API_KEY
echo

TMP_HOME=$(mktemp -d)
trap 'rm -rf "$TMP_HOME"' EXIT

HOME="$TMP_HOME" silverfin config --set-host "$HOST"
HOME="$TMP_HOME" silverfin authorize-partner -i "$PARTNER_ID" -k "$API_KEY" -n "partner-${PARTNER_ID}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

πŸ”’ Security & Privacy | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Inspecting relevant files...\n'
git ls-files scripts/authorize-partner-secret.sh README.md
printf '\n--- scripts/authorize-partner-secret.sh ---\n'
cat -n scripts/authorize-partner-secret.sh | sed -n '1,120p'
printf '\n--- README.md excerpt ---\n'
cat -n README.md | sed -n '300,330p'

printf '\n--- Searching for authorize-partner usage and supported input paths ---\n'
rg -n --hidden --no-messages 'authorize-partner|api_key|hidden input|stdin|env|secret' .

Repository: silverfin/bso_github_actions

Length of output: 24668


🌐 Web query:

Silverfin CLI authorize-partner API key stdin environment variable -k documentation

πŸ’‘ Result:

The Silverfin CLI does not have a native "authorize-partner" command that accepts an API key via stdin or environment variables as a single automated step [1][2]. Authorization for the Silverfin CLI is handled through the silverfin authorize command [1][2]. This command initiates an interactive OAuth2 flow, which requires a user to: 1. Run silverfin authorize in their terminal [1][2]. 2. Log in through a browser window that opens automatically [1][2]. 3. Copy an authorization code provided by the browser and paste it into the terminal when prompted [2]. Regarding configuration and environment variables: - API Credentials: The client_id and client_secret (obtained when registering an API application) are configured via environment variables SF_API_CLIENT_ID and SF_API_SECRET [1][3]. These are typically set in shell configuration files (e.g., ~/.bashrc or ~/.zshrc) [1][3]. - Automation: For CI/CD environments (such as GitHub Actions), automation is generally achieved by managing a configuration file (often referred to as CONFIG_JSON in examples) that stores refreshed tokens, rather than by programmatically "authorizing" a partner key via stdin [4]. If you are looking to automate authentication in a non-interactive environment, you should reference your repository's configuration management workflow, which typically involves maintaining a persistent token file [4]. There is no documented support for piping a partner API key directly into an "authorize-partner" command [1][2].

Citations:


Avoid claiming the API key stays out of argv.

  • scripts/authorize-partner-secret.sh#L46 still passes API_KEY to silverfin authorize-partner -k ..., so it can be exposed via process listings.
  • README.md#L318 should match that behavior, or the script should be changed to use a non-argv secret path.
πŸ“ Affects 2 files
  • scripts/authorize-partner-secret.sh#L46-L46 (this comment)
  • README.md#L314-L318
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/authorize-partner-secret.sh` at line 46, Align the secret-handling
behavior across scripts/authorize-partner-secret.sh:46 and README.md:314-318:
either change the authorize-partner invocation to use a non-argv secret
mechanism, or update the README to accurately state that API_KEY is passed via
-k and may appear in process listings; ensure both sites consistently document
and implement the chosen behavior.


gh secret set "PARTNER_CONFIG_JSON_${PARTNER_ID}" --repo "$REPO" < "$TMP_HOME/.silverfin/config.json"

echo "Done: PARTNER_CONFIG_JSON_${PARTNER_ID} updated on ${REPO}."
Loading