Symptom
Every unbounded release (release.yaml) run reports the same actor regardless
of who cut the release. Two runs from 2026-09-02:
| Release build |
actor |
triggering_actor |
Who actually cut it |
33655638847 (v0.5.0) |
plombardi89 |
plombardi89 on attempt 1 |
cchildress, via prepare run 33655577293 dispatched 36s earlier |
33667392561 (v0.6.0) |
plombardi89 |
plombardi89 |
cchildress, via prepare run 33667308864 dispatched 49s earlier |
The v0.6.0 case is the clean one: cchildress dispatched, and both actor and
triggering_actor came back plombardi89 on attempt 1. cchildress appears
nowhere on the run.
This holds for every release since 2026-06-23. Correlating all 80 release.yaml
runs against all 52 release-prepare runs: every release build with a matching
prepare run reports plombardi89, including ones cut by bcho, vpatelsj,
jwilder, jveski and cchildress. The only builds with a truthful actor are
the handful with no prepare run at all (v0.1.24-rc.11 through rc.18,
v0.2.0-beta.4, v0.2.3-alpha.0 through alpha.2), which were tags pushed by
hand.
Cause
release-prepare pushes the tag over SSH with RELEASE_TAG_DEPLOY_KEY rather
than GITHUB_TOKEN, because GitHub suppresses workflow triggers for tags pushed
with the default token:
https://github.com/Azure/unbounded/blob/main/.github/workflows/release-prepare.yaml#L110-L116
GitHub attributes a deploy-key push to the account that registered the key.
That key was added by an individual maintainer when 6c8d83d (#328) introduced
this mechanism, so the actor on every workflow-cut release is that person,
permanently.
triggering_actor cannot substitute: on a push it equals actor, and on a
re-run it names whoever pressed re-run. Neither field can name the cutter.
Why this is worth fixing rather than just documenting
- Audit trail. The release history names one person for releases they had
nothing to do with. For a release pipeline that signs artifacts, that is the
wrong property.
- Bus factor. The push credential is owned by an individual account. If that
account is offboarded or the key is removed, release-prepare starts failing
at the push, and the failure mode is the ugly one: a tag can land with no
build behind it (RELEASING.md, "A tag was pushed but no build started").
- Ownership. It is a repository write credential held by a person rather
than by the org.
Fix
Replace the user-owned deploy key with an org-owned identity. Either:
- GitHub App installation token, minted per run with
actions/create-github-app-token, scoped to this repo with contents: write.
Pushes made with an App token do trigger workflows, so the reason the deploy
key exists at all is preserved. Runs would then be attributed to the App's bot
identity.
- Deploy key re-issued from a machine account, which is cheaper but keeps a
long-lived key and moves the ownership problem rather than removing it.
Option 1 is preferred.
Notes
- Needs a repo admin.
GET /repos/Azure/unbounded/keys returns 404 for a
non-admin token, so confirming the current key's owner and doing the swap
cannot be done from outside admin.
relctl now works around the symptom: relctl watch <tag> reports a Cut by:
line and relctl status a BY column, both derived by correlating the build
against the release-prepare run whose execution window contains the tag push.
That is a mitigation, not a fix, and it can only report unknown for builds
older than its correlation window. It should be simplified once the underlying
attribution is correct.
Symptom
Every
unbounded release(release.yaml) run reports the sameactorregardlessof who cut the release. Two runs from 2026-09-02:
actortriggering_actorv0.5.0)plombardi89plombardi89on attempt 1cchildress, via prepare run 33655577293 dispatched 36s earlierv0.6.0)plombardi89plombardi89cchildress, via prepare run 33667308864 dispatched 49s earlierThe
v0.6.0case is the clean one: cchildress dispatched, and bothactorandtriggering_actorcame backplombardi89on attempt 1. cchildress appearsnowhere on the run.
This holds for every release since 2026-06-23. Correlating all 80
release.yamlruns against all 52
release-prepareruns: every release build with a matchingprepare run reports
plombardi89, including ones cut bybcho,vpatelsj,jwilder,jveskiandcchildress. The only builds with a truthful actor arethe handful with no prepare run at all (
v0.1.24-rc.11throughrc.18,v0.2.0-beta.4,v0.2.3-alpha.0throughalpha.2), which were tags pushed byhand.
Cause
release-preparepushes the tag over SSH withRELEASE_TAG_DEPLOY_KEYratherthan
GITHUB_TOKEN, because GitHub suppresses workflow triggers for tags pushedwith the default token:
https://github.com/Azure/unbounded/blob/main/.github/workflows/release-prepare.yaml#L110-L116
GitHub attributes a deploy-key push to the account that registered the key.
That key was added by an individual maintainer when 6c8d83d (#328) introduced
this mechanism, so the actor on every workflow-cut release is that person,
permanently.
triggering_actorcannot substitute: on a push it equalsactor, and on are-run it names whoever pressed re-run. Neither field can name the cutter.
Why this is worth fixing rather than just documenting
nothing to do with. For a release pipeline that signs artifacts, that is the
wrong property.
account is offboarded or the key is removed,
release-preparestarts failingat the push, and the failure mode is the ugly one: a tag can land with no
build behind it (
RELEASING.md, "A tag was pushed but no build started").than by the org.
Fix
Replace the user-owned deploy key with an org-owned identity. Either:
actions/create-github-app-token, scoped to this repo withcontents: write.Pushes made with an App token do trigger workflows, so the reason the deploy
key exists at all is preserved. Runs would then be attributed to the App's bot
identity.
long-lived key and moves the ownership problem rather than removing it.
Option 1 is preferred.
Notes
GET /repos/Azure/unbounded/keysreturns 404 for anon-admin token, so confirming the current key's owner and doing the swap
cannot be done from outside admin.
relctlnow works around the symptom:relctl watch <tag>reports aCut by:line and
relctl statusaBYcolumn, both derived by correlating the buildagainst the
release-preparerun whose execution window contains the tag push.That is a mitigation, not a fix, and it can only report
unknownfor buildsolder than its correlation window. It should be simplified once the underlying
attribution is correct.