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
run.secrets can still bind a few steering names: lowercase Google twins, the billing project, and pi's own agent directory #511
run.secrets refuses the provider steering variables pi and its provider SDKs read, derived from their sources (worker/src/provider-steering.mjs, re-derived for pi 0.99.1 in #509). A scan of about 180 environment names read across the 0.99.1 SDKs found a few that steer a request or a credential and are still bindable. All of them were equally bindable before #509:
Lowercase twins of reserved Google names.google_application_credentials is read by google-auth-library (build/src/auth/googleauth.js:316) and was measured planting a credential file the job then uses. gcloud_project and google_cloud_project are the same kind of twin for the project variables. The derivation compares names exactly, so the uppercase forms are refused and the lowercase ones are not.
GOOGLE_CLOUD_QUOTA_PROJECT chooses which project is billed (googleauth.js:286).
PI_CODING_AGENT_DIR: pi's own environment reads are outside the derivation entirely, so a trigger can point the job's pi at another agent directory.
Who is affected: an operator whose triggers file is edited by someone they trust less than the deployment (the file is reviewed, so this is defence in depth, not an open door).
What to build
Make the derivation case insensitive where the reading code is (google-auth reads both spellings), or add each lowercase twin the scan finds, pinned by the scan so a new twin fails the test.
Add GOOGLE_CLOUD_QUOTA_PROJECT to the reserved set through the scan, not by hand.
Extend the scan to pi-coding-agent's and pi-ai's own process.env reads, subtracting the variables the runner sets itself, and reserve the ones that change where pi reads its configuration or credentials (PI_CODING_AGENT_DIR and any siblings).
What not to do
Do not hand-type the new names: the list must stay derived from the sources, or it drifts at the next pi bump.
Do not refuse names no pinned source reads.
How to test it
worker/test/provider-steering.test.mjs: the scan finds each name above against the pinned 0.99.1 sources, and a triggers file binding any of them is refused at load; a mutation that drops the lowercase handling turns it red.
By hand: a triggers file with run.secrets naming google_application_credentials is refused by pi-dispatch doctor and by the worker at boot.
Acceptance
Every name above is refused in run.secrets, by derivation.
run.secrets now refuses every provider steering name the pinned 0.99.1 sources read. The list is derived, never hand-typed, and a two-way test checks it against the sources: 132 names (64 added, none removed), plus four hand-kept residuals.
What the scan reads:
both pi-ai copies: the hoisted one, and the runner's, nested under pi-coding-agent (google-auth-library 10.9.1 and 10.6.2);
one more dependency hop, through declared dependencies (google-auth-library, @smithy/core, the AWS chain, debug, ws);
every PI_* name pi itself reads, minus the names the worker writes and the runner sets (PI_OFFLINE, PI_TELEMETRY).
How it stays complete: comments are stripped first. Every environment access in every scanned file must then either name a variable or be counted, per file, in a pinned list. Helpers that read process.env[param] are derived from the code, so a new reading site or a new caller fails the test instead of passing silently. Forms with no env token at all (for example process["e"+"nv"]) are a stated limit; none occurs at the pin.
The names from this issue: the lowercase twins (google_application_credentials, gcloud_project, google_cloud_project), GOOGLE_CLOUD_QUOTA_PROJECT and PI_CODING_AGENT_DIR are all refused at load. Matching stays exact, so names nothing reads (openai_base_url, anthropic_api_key) stay bindable.
Found in review: reserved names were checked only when the triggers file loads. A job already queued, a stored cron template or an older receiver could still carry one. The processor now refuses such a job before the resolver runs and before any spend (secret-name-reserved).
Release notes must say: a triggers file binding a newly reserved name stops loading. Some of those are ordinary names: DEBUG, K_CONFIGURATION, AWS_EXECUTION_ENV, PI_TUI_DEBUG. Put them on PI_FORWARD_ENV instead.
Activity
fix(triggers): run.secrets refuses every steering name the pinned sou…
fix(triggers): run.secrets refuses every steering name the pinned sou…
fix(triggers): run.secrets refuses every steering name the pinned sou…
fix(triggers): run.secrets refuses every steering name the pinned sou…
fix(triggers): run.secrets refuses every steering name the pinned sou…
edgehero commentedon Oct 2, 2026
Fixed in #513 (9449e69).
run.secretsnow refuses every provider steering name the pinned 0.99.1 sources read. The list is derived, never hand-typed, and a two-way test checks it against the sources: 132 names (64 added, none removed), plus four hand-kept residuals.dependencies(google-auth-library, @smithy/core, the AWS chain, debug, ws);PI_*name pi itself reads, minus the names the worker writes and the runner sets (PI_OFFLINE,PI_TELEMETRY).process.env[param]are derived from the code, so a new reading site or a new caller fails the test instead of passing silently. Forms with noenvtoken at all (for exampleprocess["e"+"nv"]) are a stated limit; none occurs at the pin.google_application_credentials,gcloud_project,google_cloud_project),GOOGLE_CLOUD_QUOTA_PROJECTandPI_CODING_AGENT_DIRare all refused at load. Matching stays exact, so names nothing reads (openai_base_url,anthropic_api_key) stay bindable.secret-name-reserved).DEBUG,K_CONFIGURATION,AWS_EXECUTION_ENV,PI_TUI_DEBUG. Put them onPI_FORWARD_ENVinstead.