Skip to content

feat: add wings_enabled input (mise-wings cache integration)#454

Merged
jdx merged 1 commit into
mainfrom
feat/wings
Apr 29, 2026
Merged

feat: add wings_enabled input (mise-wings cache integration)#454
jdx merged 1 commit into
mainfrom
feat/wings

Conversation

@jdx

@jdx jdx commented Apr 28, 2026

Copy link
Copy Markdown
Owner

Summary

Adds two new inputs that gate the mise-wings asset cache for tool installs. Existing workflows are unaffected: default wings_enabled: false is a no-op.

Input Default Description
wings_enabled false Route tool-install URLs through the wings cache when true

How it works

When wings_enabled: true, the action exports MISE_WINGS_ENABLED=1. Authentication is fully automatic — mise itself owns the GHA OIDC → wings session exchange. No mise wings login step in workflow YAML, no long-lived secrets to rotate.

When mise (built with wings support — see jdx/mise#9458) sees MISE_WINGS_ENABLED=1 and detects the GHA OIDC env vars (ACTIONS_ID_TOKEN_REQUEST_URL + ACTIONS_ID_TOKEN_REQUEST_TOKEN), it:

  1. Fetches the runner's OIDC token, scoped to the wings deployment audience
  2. POSTs it to https://api.<host>/auth to mint a wings CI session JWT
  3. Caches the JWT in-process for the rest of the workflow run
  4. Transparently rewrites registry.npmjs.org / github.com / api.github.com URLs to the corresponding wings cache subdomains and attaches the JWT as a Bearer header

Why opt-in (not opt-out)

The default-off posture is deliberate. Many workflows already declare permissions: id-token: write for unrelated reasons (SLSA provenance, AWS OIDC, Sigstore, npm provenance, etc.). If wings_enabled defaulted to true, those workflows would silently send the runner's OIDC identity claims to a third-party cache without explicit consent. Cursor Bugbot HIGH + Greptile P1+security correctly flagged the previous "default true" iteration of this PR as a privacy regression.

Explicit opt-in keeps the gate visible in the workflow YAML.

Workflow requirements

permissions:
  id-token: write   # required for OIDC

jobs:
  build:
    steps:
      - uses: jdx/mise-action@<sha>
        with:
          wings_enabled: true

The action emits a clear warning when wings_enabled: true but id-token: write is missing — without that hint, the user would see "wings configured but doing nothing" and have no clue why.

Test plan

  • npm run all — format + lint + package, clean
  • dist/index.js rebuilt and contains the wings hook (greppable: MISE_WINGS_ENABLED, setupWings)
  • End-to-end: a workflow with wings_enabled: true, permissions: id-token: write, an active wings subscription, and a recent enough mise binary. The mise repo's own docs.yml will exercise this path once jdx/mise#9458 is merged.
  • Default-off path: a workflow without the wings_enabled input behaves identically to today.

Out of scope

  • Older mise binaries will see MISE_WINGS_ENABLED and silently ignore it (no wings client code) — that's intended; the action doesn't gate on mise version.
  • Self-hosted runners: permissions: id-token: write only does anything on GitHub-hosted runners by default. Self-hosted runners need extra config; the warning above is conservative enough for both cases.

🤖 Generated with Claude Code


Note

Medium Risk
Introduces an opt-in path that can cause OIDC-based authentication to a third-party cache and alters tool download routing when enabled. Default-off behavior limits impact, but misconfiguration could create confusing cache bypass or unexpected network/token exchange behavior.

Overview
Adds a new experimental wings_enabled action input (default false) to opt workflows into the mise-wings asset cache by exporting MISE_WINGS_ENABLED=1.

When enabled, the action now runs setupWings() early to set the env var and warn if GitHub OIDC env vars are missing (i.e., permissions: id-token: write not configured), while leaving existing/default behavior unchanged.

Reviewed by Cursor Bugbot for commit 969042f. Bugbot is set up for automated code reviews on this repo. Configure here.

jdx added a commit to jdx/mise that referenced this pull request Apr 28, 2026
Adds the CI-side authentication path so workflows on a Clerk-org-
linked GitHub repo can use mise-wings caching without a `mise wings
login` step or any long-lived secret.

## How

New `crate::wings::ci` module. When the HTTP hook sees:

  - `wings.enabled = true`
  - no on-disk dev credentials (so the dev path doesn't apply)
  - GHA OIDC env vars present (`ACTIONS_ID_TOKEN_REQUEST_URL` +
    `ACTIONS_ID_TOKEN_REQUEST_TOKEN`, populated when the workflow
    declares `permissions: id-token: write`)

…it lazily exchanges the runner's OIDC token for a wings session
JWT via the proxy's `POST /auth` route, then caches the JWT in a
`tokio::sync::OnceCell<String>` for the rest of the process.

Two HTTP calls, both fail-open (warn log, no error propagation):

  1. `GET <ACTIONS_ID_TOKEN_REQUEST_URL>?audience=<wings.host>`
     with the runner's bearer secret → `{value: "<jwt>"}`
  2. `POST https://api.<wings.host>/auth` with the JWT as bearer
     and an empty body → `{token, expires_in, token_type}`

The audience query parameter feeds the proxy's `EXPECTED_AUDIENCE`
env var, which is set to `wings.host` in the production Terraform
(`mise-wings.en.dev` for prod, `mise-wings-staging.en.dev` for
staging). A staging `wings.host` setting just works without
further config.

CI sessions don't carry a refresh token (the proxy's `/auth` route
is access-only), and the OIDC token is short-lived enough that
re-minting is cheap. We do it once per `mise` process. Worst case:
a single `mise install` run that takes longer than the wings
session TTL (~6 h default) hits a 401 on the last download; the
user retries.

## Why no env var "set the token here"

Earlier draft of this PR carried a `MISE_WINGS_TOKEN` env var so
mise-action could pre-mint the session and pass it through. User
correctly flagged that as the wrong shape — the action shouldn't
be doing identity work. Reverted; mise-action just exports
`MISE_WINGS_ENABLED=1` and mise figures the rest out.

## docs.yml dogfood

Updates `.github/workflows/docs.yml` to pin `jdx/mise-action` at
the SHA of jdx/mise-action#454 (the matching `wings_enabled`
input) and adds `permissions: id-token: write`. The workflow is
the canary — npm tarballs (bun) and any GitHub release downloads
route through the wings cache automatically. If wings is
unreachable or the proxy 401s, the cache subdomain falls back to
the upstream origin, so the worst case is "no acceleration",
not "build broken".

801 / 801 tests pass; clippy + fmt clean. New
`wings::ci::gha_runner_present` test pins the env-var detection.
@greptile-apps

greptile-apps Bot commented Apr 28, 2026

Copy link
Copy Markdown

Greptile Summary

Adds an opt-in wings_enabled input (default false) that exports MISE_WINGS_ENABLED=1, letting a wings-capable mise binary route tool-install downloads through the mise-wings cache using automatic GHA OIDC authentication. The implementation is minimal and correct — core.exportVariable sets the value in both process.env (for the current step) and GITHUB_ENV (for subsequent steps), so every mise install invocation in the action will see the flag.

Confidence Score: 5/5

Safe to merge — the change is additive, default-off, and the only finding is a minor comment cleanup.

No P0 or P1 issues. The single finding is a P2 style nit (internal tool names left in a comment). The core logic is correct: core.exportVariable sets the value in both process.env for the current step and GITHUB_ENV for subsequent steps, ordering relative to setupMise is harmless, and the OIDC pre-flight warning gives users actionable guidance.

No files require special attention.

Important Files Changed

Filename Overview
src/index.ts Adds setupWings() synchronous hook that exports MISE_WINGS_ENABLED=1 and warns when OIDC env vars are absent; logic is correct and default-off.
action.yml Adds wings_enabled input with well-documented description and safe false default; no breaking changes to existing inputs.

Sequence Diagram

sequenceDiagram
    participant W as Workflow YAML
    participant A as mise-action (dist/index.js)
    participant E as GITHUB_ENV / process.env
    participant M as mise binary
    participant P as wings proxy (mise-wings.en.dev)
    participant O as Origin (npm/github)

    W->>A: wings_enabled: true
    A->>A: setupWings()
    A->>E: export MISE_WINGS_ENABLED=1
    A-->>W: warn if OIDC env vars absent
    A->>M: setupMise() — curl download (unaffected by wings)
    A->>M: mise install ...
    M->>M: detect MISE_WINGS_ENABLED=1 + OIDC env vars
    M->>P: POST /auth (OIDC token exchange)
    P-->>M: wings session JWT
    M->>P: tool download via wings subdomain + Bearer JWT
    P->>O: cache miss → forward to origin
    P-->>M: cached artifact
Loading

Fix All in Claude Code

Reviews (5): Last reviewed commit: "feat: add wings_enabled input (mise-wing..." | Re-trigger Greptile

Comment thread src/index.ts Outdated
Comment thread src/index.ts Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces experimental support for mise-wings, a regional asset cache for tool installs. It adds new configuration inputs (wings_enabled, wings_host) and a setupWings function that exports the environment variables required for mise to utilize the cache via GitHub Actions OIDC authentication. Feedback was provided regarding a misleading comment: the initial download of the mise binary itself is performed via curl and will not be accelerated by mise-wings, as curl does not recognize the MISE_WINGS_ENABLED environment variable.

Comment thread src/index.ts Outdated
Comment on lines +60 to +62
// `setupMise` so any download triggered by `setupMise`
// itself (e.g. fetching the mise binary) doesn't slip
// through unaccelerated. Auth is the runner's GHA OIDC

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment suggests that setupWings is called before setupMise to accelerate the download of the mise binary itself. However, setupMise uses curl (lines 314, 321, 327, 331) to fetch the binary. curl is unaware of the MISE_WINGS_ENABLED environment variable and does not perform URL rewriting. Acceleration via mise-wings only occurs when mise itself (or its internal HTTP layer) is executing the download, such as during mise self-update or mise install. The comment should be clarified to avoid misleading users about what is actually accelerated during the initial setup.

@jdx jdx changed the title feat: add wings_enabled / wings_host inputs (mise-wings cache integration) feat: add wings_disabled input (opt-out for mise's auto-wings) Apr 29, 2026
@jdx jdx changed the title feat: add wings_disabled input (opt-out for mise's auto-wings) feat: add wings_enabled input (mise-wings cache integration) Apr 29, 2026
Comment thread action.yml

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ee7f745. Configure here.

Comment thread dist/index.js Outdated
Adds a single new input — `wings_enabled` — that gates the
[mise-wings](https://mise-wings.en.dev) asset cache for tool
installs. Existing workflows are unaffected: default `false`
is a no-op.

| Input | Default | Description |
|---|---|---|
| `wings_enabled` | `false` | Route tool-install URLs through the wings cache when `true` |

## How it works

When `wings_enabled: true`, the action exports
`MISE_WINGS_ENABLED=1`. Authentication is fully automatic —
mise itself owns the GHA OIDC → wings session exchange. No
`mise wings login` step in workflow YAML, no long-lived
secrets to rotate.

When mise (built with wings support — see jdx/mise#9458)
sees `MISE_WINGS_ENABLED=1` and detects the GHA OIDC env
vars (`ACTIONS_ID_TOKEN_REQUEST_URL` +
`ACTIONS_ID_TOKEN_REQUEST_TOKEN`), it:

  1. Fetches the runner's OIDC token, scoped to the wings
     deployment audience
  2. POSTs it to `https://api.<host>/auth` to mint a wings
     CI session JWT
  3. Caches the JWT in-process for the rest of the workflow
  4. Transparently rewrites `registry.npmjs.org` /
     `github.com` / `api.github.com` URLs to the wings
     cache subdomains and attaches the JWT as a Bearer
     header

## Why opt-in (not opt-out)

The default-off posture is deliberate. Many workflows
already declare `permissions: id-token: write` for unrelated
reasons (SLSA provenance, AWS OIDC, Sigstore, npm
provenance). If `wings_enabled` defaulted to `true`, those
workflows would silently send the runner's OIDC identity
claims to a third-party cache without explicit consent.
Cursor Bugbot HIGH + Greptile P1+security flagged a prior
"default true" iteration of this PR as a privacy
regression.

Explicit opt-in keeps the gate visible in the workflow YAML.

## Workflow requirements

```yaml
permissions:
  id-token: write   # required for OIDC

jobs:
  build:
    steps:
      - uses: jdx/mise-action@<sha>
        with:
          wings_enabled: true
```

The action emits a clear warning when `wings_enabled: true`
but `id-token: write` is missing — without that hint, the
user would see "wings configured but doing nothing" and have
no clue why.

## Notes

- Older mise binaries see `MISE_WINGS_ENABLED` and silently
  ignore it — forward-compatible.
- `setupMise` fetches the mise binary itself with `curl`,
  which doesn't go through mise's HTTP layer; the wings
  rewriter only kicks in once the resulting mise binary
  runs `mise install`. The action sets the env var before
  any `mise` subcommand runs.
@jdx jdx merged commit ac8a641 into main Apr 29, 2026
21 checks passed
@jdx jdx deleted the feat/wings branch April 29, 2026 14:36
@jdx jdx mentioned this pull request Apr 29, 2026
jdx added a commit to jdx/mise that referenced this pull request Apr 29, 2026
Adds the CI-side authentication path so workflows on a Clerk-org-
linked GitHub repo can use mise-wings caching without a `mise wings
login` step or any long-lived secret.

## How

New `crate::wings::ci` module. When the HTTP hook sees:

  - `wings.enabled = true`
  - no on-disk dev credentials (so the dev path doesn't apply)
  - GHA OIDC env vars present (`ACTIONS_ID_TOKEN_REQUEST_URL` +
    `ACTIONS_ID_TOKEN_REQUEST_TOKEN`, populated when the workflow
    declares `permissions: id-token: write`)

…it lazily exchanges the runner's OIDC token for a wings session
JWT via the proxy's `POST /auth` route, then caches the JWT in a
`tokio::sync::OnceCell<String>` for the rest of the process.

Two HTTP calls, both fail-open (warn log, no error propagation):

  1. `GET <ACTIONS_ID_TOKEN_REQUEST_URL>?audience=<wings.host>`
     with the runner's bearer secret → `{value: "<jwt>"}`
  2. `POST https://api.<wings.host>/auth` with the JWT as bearer
     and an empty body → `{token, expires_in, token_type}`

The audience query parameter feeds the proxy's `EXPECTED_AUDIENCE`
env var, which is set to `wings.host` in the production Terraform
(`mise-wings.en.dev` for prod, `mise-wings-staging.en.dev` for
staging). A staging `wings.host` setting just works without
further config.

CI sessions don't carry a refresh token (the proxy's `/auth` route
is access-only), and the OIDC token is short-lived enough that
re-minting is cheap. We do it once per `mise` process. Worst case:
a single `mise install` run that takes longer than the wings
session TTL (~6 h default) hits a 401 on the last download; the
user retries.

## Why no env var "set the token here"

Earlier draft of this PR carried a `MISE_WINGS_TOKEN` env var so
mise-action could pre-mint the session and pass it through. User
correctly flagged that as the wrong shape — the action shouldn't
be doing identity work. Reverted; mise-action just exports
`MISE_WINGS_ENABLED=1` and mise figures the rest out.

## docs.yml dogfood

Updates `.github/workflows/docs.yml` to pin `jdx/mise-action` at
the SHA of jdx/mise-action#454 (the matching `wings_enabled`
input) and adds `permissions: id-token: write`. The workflow is
the canary — npm tarballs (bun) and any GitHub release downloads
route through the wings cache automatically. If wings is
unreachable or the proxy 401s, the cache subdomain falls back to
the upstream origin, so the worst case is "no acceleration",
not "build broken".

801 / 801 tests pass; clippy + fmt clean. New
`wings::ci::gha_runner_present` test pins the env-var detection.
@jdx jdx mentioned this pull request May 22, 2026
@jdx jdx mentioned this pull request Jun 4, 2026
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