You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Runner refresh exchange always 400s; keep a renewable credential for long-running apps #39
Source: nordiccare report (Slack #C0BS2FZ1YE7, 2026-11-05): "the app's token expires after an hour". Investigation shows the premise differs from what the entrypoint intends.
Not a regression, but a blocker for the hardening
Nothing changed recently. The header-only call has never matched what POST /runner-token/refresh expects (JSON body required since the endpoint was added on 2026-02-25; the runner block predates 2026-06-08, per git history, not re-run against old versions). It stays invisible because || true swallows the failure and the 365-day runner-refresh token stays in OSC_ACCESS_TOKEN.
This must be fixed before REJECT_RUNNER_REFRESH_TOKENS is enabled (token-service #116, default false, not set in infra-osaas). Once it is on, apps and sibling runners that rely on that token stop working. Blocks: enabling that flag in prod.
Findings (from code reading; the 400/401 was probed against dev and prod with a junk token)
The OSC_ACCESS_TOKEN a python My App receives is a runner-refresh token (type: runner-refresh, scope config-access, 365 days; osaas-lib-token src/pat/token.ts:48-73).
scripts/docker-entrypoint.sh:214-218 tries to exchange it at POST token.svc/runner-token/refresh for a 1h PAT, but sends the token only in an x-pat-jwt header. The endpoint requires a JSON body {"token": ...} (token-service src/model.ts:29-31): header only gives 400 body must be object.
curl -sf ... || true swallows the failure, so the 365-day token stays in the env var. Apps work today by accident; they depend on REJECT_RUNNER_REFRESH_TOKENS staying false (osaas-deploy-manager src/server.ts:219).
Sibling runners: wasm-runner (:128-130) has the same bug; golang-runner (:131-133) and dotnet-runner (:137-139) send a Bearer header with no body and also fail; web-runner (:303-305) and php-runner (:116-118) are correct.
Change (contract change, needs birme approval before merge)
Fixing the body alone turns today's 365-day env token into a 1h PAT and breaks any app using it after an hour. The two parts must ship together:
POST {"token": $OSC_ACCESS_TOKEN}, as web-runner does.
Keep the original token available as OSC_REFRESH_TOKEN so the app can call /runner-token/refresh itself; document with a Python snippet.
Log a [CONFIG] warning on a failed exchange instead of || true.
Acceptance criteria
Items 1-3 above, in one PR
Tests for the success and failure paths
Consumer audit for apps reading OSC_ACCESS_TOKEN, and Contract change approved by birme: <url> on this ticket before merge
Sibling tickets filed for wasm-, golang- and dotnet-runner (linked below)
Activity
birme commentedon Nov 6, 2026
Sibling added for python-job-runner: Eyevinn/python-job-runner#6 (the audit's seventh runner; wasm, golang, dotnet are #37, #32, #31).