Skip to content

Decouple token refresh from liquid tests: read-only CONFIG_JSON in run_tests.yml - #40

Draft
michieldegezelle wants to merge 1 commit into
mainfrom
ci-decouple-auth-from-tests
Draft

Decouple token refresh from liquid tests: read-only CONFIG_JSON in run_tests.yml#40
michieldegezelle wants to merge 1 commit into
mainfrom
ci-decouple-auth-from-tests

Conversation

@michieldegezelle

Copy link
Copy Markdown
Collaborator

⛔ Do not merge yet — prerequisite PRs must land and be verified first

This makes the shared run_tests.yml stop refreshing tokens. If it merges before a caller repo has
its own scheduled refresher live, nothing refreshes that repo's CONFIG_JSON and every firm's
access token lapses within its 2h TTL, failing all of its liquid tests. Opened as a draft for that
reason.

Prerequisites, one per caller of this workflow:

  • nl_market#933 merged, and a schedule-triggered refresh_token.yml run confirmed green
  • uk_market#1552 merged + confirmed
  • lu_market#820 merged + confirmed
  • be_market — already live since 2026-07-24 (be_market#3111 only switches its uses: ref to
    @main); it forks run_tests.yml inline and is unaffected by this PR either way

What changes

.github/workflows/run_tests.yml becomes a read-only consumer of CONFIG_JSON:

  • the check-auth job (and test-templates' needs: on it) is removed — this workflow no longer
    refreshes tokens or writes the secret;
  • the config load blanks every firm's refreshToken via jq before the CLI runs, and passes the
    secret through env: rather than interpolating it into the script text;
  • a header comment records the design, the prerequisite, and the known CLI-side limitation.

README.md gains a "Token refresh (refresh_token.yml in each market repo)" section under
Authentication: why the split exists, the per-repo adopter checklist, and the two known
limitations. This also closes out the "push_to_review_firm adopter checklist" action item, since
the checklist is the same for either consumer.

Why

silverfin-cli refreshes an access token transparently on any 401, for any command, and
Silverfin rotates the refresh token server-side when it does. The rotated pair is written only to
the runner's local ~/.silverfin/config.json — there is no path back to the secret — so
CONFIG_JSON is left holding an already-consumed refresh token. The next explicit refresh fails
with invalid_grant and that firm needs a manual re-authorization before its CI works again.

That is the 2026-07-14 incident on firm 400583. Two decoupled writers racing over one rotating
credential cannot be made safe by ordering them; the fix is one scheduled writer plus consumers
that are structurally incapable of rotating anything. With refreshToken blanked, a stale access
token fails its 401 cleanly instead of poisoning the secret.

Validation

Not new logic — this ports what has been running in production:

  • be_market#3047 forked this exact design inline on 2026-07-24 and has run it since. Over the
    most recent ~2.5 day window: 199/200 refresh_token.yml runs green (the one failure self-healed
    on the next tick and did fire its Slack alert), with run_tests.yml green on live PR traffic
    throughout.
  • The same jq strip has been live in push_to_review_firm.yml since push_to_review_firm.yml: strip refreshToken to stop silent CONFIG_JSON poisoning #29 (2026-07-23), including
    a forced-401 drill that confirmed a stripped run fails cleanly and leaves the firm's real refresh
    token untouched.
  • Earlier end-to-end validation of the read-only + cron split (including a token-survival probe
    proving a held access token stays valid across an external refresh, and 5 concurrent read-only
    runs passing in parallel) is in silverfin-cli/CI_AUTH_SAMPLER_PLAN.md §1.

Post-merge verification

  1. Next PR in nl/uk/lu: run-tests / test-templates runs green, and its log shows the
    "(read-only, no-poison)" load step. There should no longer be a check-auth job in the run.
  2. refresh_token.yml in each repo keeps ticking green and CONFIG_JSON's timestamp keeps moving.
  3. No invalid_grant in any repo's refresh logs over the following days — the same check run for
    be_market's weekend window on 2026-07-27, which was clean across 116 runs and all 8 firms.

Deliberately not in scope

  • Per-firm fault isolation in check_auth.yml. Under bash -e, one firm's failed refresh
    aborts the step and skips the write-back, so no firm's refreshed token persists for that tick.
    It self-heals on the next tick and only bites on a genuinely dead token, which the Slack alert
    surfaces — fail-safe, not fail-dangerous. Changing the shared secret-writer in the same change
    as a four-repo rollout is the wrong trade; separate PR.
  • The CLI batch-kill (#267/#268, both closed unmerged). A stale token still makes
    silverfin-cli exit the whole process, losing the verdict for every handle batched in the same
    run-test --status call. CI-reliability only — this job has no write-back path, so it can never
    poison a token. Documented in the workflow header.
  • Pinning the CLI install. Still npm install …/silverfin-cli.git unpinned, matching every
    other production workflow; be_market has run floating main for a month.
  • Other writers in this repo. update_templates_* and add_shared_parts_* are unchanged and
    out of scope here.

Drops the check-auth job and loads CONFIG_JSON with every firm's
refreshToken blanked, making this workflow a read-only consumer that can
never rotate or write the shared token. Token refreshing moves to a
scheduled refresh_token.yml in each market repo, which becomes that repo's
sole CONFIG_JSON writer.

Removes the poisoning class behind the 2026-07-14 invalid_grant incident on
firm 400583: silverfin-cli refreshes on any 401 and Silverfin rotates the
refresh token server-side, but the rotated pair dies with the runner, so the
secret keeps a consumed token until a human re-authorizes the firm.

Validated in be_market since 2026-07-24 (forked inline there in #3047);
same strip mechanism as push_to_review_firm.yml (#29). README documents the
pattern and the per-repo adopter checklist.

PREREQUISITE, do not merge before: every caller repo needs its cron live
first (nl#933, uk#1552, lu#820), or its tokens stop being refreshed and
every firm's access token lapses within its 2h TTL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant