Summary
Let a manifest back a token reference (state_token, release_token) with a GitHub App identity, not just a static secret. The generator would mint a short-lived installation token at runtime and wire it into the steps that need it.
Background
state_token and release_token currently accept a static secret expression, e.g. state_token: ${{ secrets.CASCADE_STATE_TOKEN }} (added in #94, wired for the self-host in #96). A static secret works for a Personal Access Token, which is what the self-host uses today to write manifest state to the protected trunk.
A GitHub App is the better identity for this job, both for cascade's own pipeline and for anyone self-hosting cascade on a protected branch:
- It is owned by an org or repo, not a person, so it does not break when an individual rotates a PAT or leaves.
- Tokens are minted per run and expire in about an hour, so no long-lived push-capable secret sits on the trunk. Only the App private key is stored, and it can be rotated independently.
- Commits and dispatches show as the App identity with a clear audit trail.
- An App can be added to a repository ruleset bypass list directly, instead of relying on
enforce_admins being off plus an admin-owned PAT.
The blocker is that an App token is not static: it is produced at runtime by exchanging the App ID and private key (for example via actions/create-github-app-token), and surfaces as a step output rather than a secret.
Proposed approach
- Introduce a token-source abstraction so a token reference can be either a static secret (today's behavior) or an App source (
app_id + private_key secrets). Additive and non-breaking; existing manifests keep working unchanged.
- When a token source is an App, the generator emits a token-minting step before the consuming step and references the minted output.
- Keep the existing real-GitHub vs gitea split: skip the minting step on the act/gitea e2e path, which has no GitHub App, and fall back to the current behavior there.
- Apply uniformly to every token seam (
state_token, release_token) so the policy is consistent.
Out of scope / operator setup
Creating the App, installing it, and adding it to a ruleset bypass list are one-time operator steps, documented but not automated.
Acceptance
Summary
Let a manifest back a token reference (
state_token,release_token) with a GitHub App identity, not just a static secret. The generator would mint a short-lived installation token at runtime and wire it into the steps that need it.Background
state_tokenandrelease_tokencurrently accept a static secret expression, e.g.state_token: ${{ secrets.CASCADE_STATE_TOKEN }}(added in #94, wired for the self-host in #96). A static secret works for a Personal Access Token, which is what the self-host uses today to write manifest state to the protected trunk.A GitHub App is the better identity for this job, both for cascade's own pipeline and for anyone self-hosting cascade on a protected branch:
enforce_adminsbeing off plus an admin-owned PAT.The blocker is that an App token is not static: it is produced at runtime by exchanging the App ID and private key (for example via
actions/create-github-app-token), and surfaces as a step output rather than a secret.Proposed approach
app_id+private_keysecrets). Additive and non-breaking; existing manifests keep working unchanged.state_token,release_token) so the policy is consistent.Out of scope / operator setup
Creating the App, installing it, and adding it to a ruleset bypass list are one-time operator steps, documented but not automated.
Acceptance
state_tokenandrelease_token.