Skip to content

Commit cd71afc

Browse files
Add zero-config free tier via GitHub OIDC + hosted proxy
The action required an llm_api_key secret. It can now run with NO secret on a free hosted tier: when the workflow grants `permissions: id-token: write` and no key/license is given, the action mints a GitHub Actions OIDC JWT and points the engine's OPENROUTER_BASE_URL at CodeBoarding's gha_proxy, which verifies the token, meters per repository owner against a weekly cap, and swaps in the real OpenRouter key. A user key or a CodeBoarding license remain the more-usage paths. Backward compatible — stays on @v1. A secret-based workflow behaves identically (no OIDC, no base-url override, same provider preflight). Changes: - Inputs: llm_api_key now optional (default ''); add proxy_url (the gha_proxy Function URL) and license_key. - "Prepare & verify LLM key": three credential modes in precedence order — byokey (current behavior verbatim) / license (bearer = license, base-url = proxy) / oidc (mint from ACTIONS_ID_TOKEN_REQUEST_URL/_TOKEN with &audience=codeboarding-proxy, base-url = proxy). Hosted modes pin OpenRouter + default models and skip the openrouter.ai preflight; the JWT/license is masked. Writes a `mode` output. - Engine-run steps (base/head/analyze): export OPENROUTER_BASE_URL from cb-base-url when present (hosted modes only); export CB_QUOTA_SENTINEL so the adapter can flag a 402. cb-base-url added to the key-material cleanup. - Cache key folds in the credential mode so switching free-tier <-> BYO key never reuses the other mode's cached base analysis. - engine_adapter.py: detect HTTP 402 / "Resource exhausted: token limit reached" (status attr or cause chain) and drop a cb-quota-exhausted sentinel, then re-raise so the step still fails. The failure-comment step branches on the sentinel to post a "free weekly limit reached — add a key/license" comment. - README: zero-config quick start (no secret, requires id-token: write), a "More usage" section for the key/license paths, updated inputs table. - Tests: 8 new (quota detection by status/marker/cause-chain; main() drops the sentinel on 402 and not on other errors). Full suite 156, all green. The 200/402 paths against the dev proxy need a real id-token: write workflow; exercised separately. The proxy is licensing-aws#10 (deploy prod + bake the prod proxy_url default before merging this). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3a21003 commit cd71afc

4 files changed

Lines changed: 326 additions & 52 deletions

File tree

README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ permissions:
7777
contents: write
7878
pull-requests: write
7979
issues: write
80+
# Lets the action mint a short-lived GitHub OIDC token so the free hosted tier
81+
# can identify your repository. Required for the no-secret (free-tier) path;
82+
# harmless to keep when you bring your own key.
83+
id-token: write
8084

8185
concurrency:
8286
group: codeboarding-${{ github.event.pull_request.number || github.event.issue.number }}
@@ -95,27 +99,33 @@ jobs:
9599
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
96100
steps:
97101
- uses: CodeBoarding/CodeBoarding-action@v1
98-
with:
99-
llm_api_key: ${{ secrets.OPENROUTER_API_KEY }}
100102
```
101103
102-
Add the API key as a [repository secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets) (Settings → Secrets and variables → Actions):
104+
That's it — **no API key needed**. With `id-token: write` granted, the action runs on the **free hosted tier**: it mints a GitHub OIDC token, and CodeBoarding's proxy supplies the LLM, metered per repository owner against a weekly cap. Merge the workflow and your next pull request gets an architecture diff.
103105

104-
```text
105-
OPENROUTER_API_KEY = sk-or-...
106+
Models are optional. Omit `agent_model` and `parsing_model` to use the defaults, or pin them inline or from a repository variable (a model name is not a secret, so use `vars.`, not `secrets.`):
107+
108+
```yaml
109+
with:
110+
agent_model: anthropic/claude-sonnet-4 # optional; or ${{ vars.AGENT_MODEL }}
111+
parsing_model: google/gemini-3-flash-preview # optional
106112
```
107113

108-
That is the only required setup, passed via `llm_api_key` above. For local runs with `scripts/run_local.sh`, export `OPENROUTER_API_KEY` as an environment variable instead.
114+
<a id="more-usage"></a>
115+
## More usage (your own key or a license)
109116

110-
Models are optional. Omit `agent_model` and `parsing_model` to use the engine's default for your provider, or pin them inline or from a repository variable (a model name is not a secret, so use `vars.`, not `secrets.`):
117+
The free tier is metered per repository owner against a weekly cap. For more — or unmetered — usage, supply a credential. Both paths skip the proxy/OIDC and need no `id-token: write`:
111118

112119
```yaml
113120
with:
114-
llm_api_key: ${{ secrets.OPENROUTER_API_KEY }} # secret
115-
agent_model: anthropic/claude-sonnet-4 # optional; or ${{ vars.AGENT_MODEL }}
116-
parsing_model: google/gemini-3-flash-preview # optional
121+
# Option A — your own OpenRouter key (talks to OpenRouter directly):
122+
llm_api_key: ${{ secrets.OPENROUTER_API_KEY }}
123+
# Option B — a CodeBoarding license (unmetered hosted usage):
124+
# license_key: ${{ secrets.CODEBOARDING_LICENSE }}
117125
```
118126

127+
Add the secret under **Settings → Secrets and variables → Actions** (e.g. `OPENROUTER_API_KEY = sk-or-...`). For local runs with `scripts/run_local.sh`, export `OPENROUTER_API_KEY` as an environment variable instead. When `llm_api_key` is set it takes precedence; `license_key` is used only when no key is set; with neither, the free OIDC tier is used.
128+
119129
## Bring your own LLM provider
120130

121131
OpenRouter is the default, but you can use any provider the engine supports. Set `llm_provider` and pass that provider's key:
@@ -193,6 +203,7 @@ on:
193203
194204
permissions:
195205
contents: write # commit the generated docs to the branch
206+
id-token: write # free hosted tier (omit if you set llm_api_key/license_key)
196207
197208
concurrency:
198209
group: codeboarding-sync
@@ -206,7 +217,8 @@ jobs:
206217
- uses: CodeBoarding/CodeBoarding-action@v1
207218
with:
208219
mode: sync
209-
llm_api_key: ${{ secrets.OPENROUTER_API_KEY }}
220+
# No key needed on the free tier. For more/unmetered usage add
221+
# `llm_api_key: ${{ secrets.OPENROUTER_API_KEY }}` (and drop id-token: write).
210222
```
211223

212224
Behavior worth knowing:
@@ -240,8 +252,10 @@ Be aware that `contents: write` is repo-wide — GitHub does not scope it to a b
240252

241253
| Input | Mode | Default | Description |
242254
|---|---|---|---|
243-
| `llm_api_key` | both | required | Your LLM provider API key (see `llm_provider`). |
244-
| `llm_provider` | both | `openrouter` | Provider for the key, mapped to `<NAME>_API_KEY` (e.g. `anthropic`, `openai`, `google`). |
255+
| `llm_api_key` | both | empty | Your LLM provider API key (see `llm_provider`). Leave empty to use the free hosted tier via a GitHub OIDC token (needs `permissions: id-token: write`). |
256+
| `llm_provider` | both | `openrouter` | Provider for the key, mapped to `<NAME>_API_KEY` (e.g. `anthropic`, `openai`, `google`). Ignored on the free/license hosted tier (always OpenRouter via the proxy). |
257+
| `license_key` | both | empty | A CodeBoarding license for unmetered hosted usage. Used when `llm_api_key` is empty; takes precedence over the free tier. |
258+
| `proxy_url` | both | CodeBoarding proxy | Hosted LLM proxy base URL for the free/license tiers (the engine's `OPENROUTER_BASE_URL`). Override only for a self-hosted/dev proxy. |
245259
| `mode` | both | `review` | `review` posts the PR architecture-diff comment; `sync` analyzes on push and commits the architecture (`analysis.json` + rendered docs) to `target_branch`, keeping it versioned and current. |
246260
| `github_token` | both | `${{ github.token }}` | Token for GitHub API calls; in review mode it posts or updates the PR comment. |
247261
| `push_token` | both | `${{ github.token }}` | Token used for pushes: in review mode the generated `analysis.json` to the PR branch (for the webview link), in sync mode the architecture to `target_branch`. The workflow token can push when the workflow grants `permissions: contents: write`. Separate from `github_token` so commenting can use a GitHub App token while the push uses the workflow token. |

0 commit comments

Comments
 (0)