Skip to content

ci: ci+docs: real offline-licensed CI via a test-only aidn2 key (#15)#1915

Merged
ooples merged 3 commits into
masterfrom
docs/ci-license-rotation
Jul 21, 2026
Merged

ci: ci+docs: real offline-licensed CI via a test-only aidn2 key (#15)#1915
ooples merged 3 commits into
masterfrom
docs/ci-license-rotation

Conversation

@ooples

@ooples ooples commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Completes task #15 — durable, real offline-licensed CI on a separate, test-only aidn2 key that never ships in published packages.

Already shipped in #1891 (verified, not rebuilt)

  • csproj embeds BuildKey/LicensePublicKey.json; LicensePublicKeyProvider supports multi-kid rotation.
  • keygen.py / aidn2_issuer.py tooling.
  • The test ModuleInitializer keeps an offline-Active, model:save-capable AIDOTNET_LICENSE_KEY (the CI aidn2 path) and otherwise installs a synthetic offline license — so this change is non-breaking either way.

This PR

  • tools/license-issuer/README.md — issuance + rotation guide, incl. the trust-scope rationale.
  • sonarcloud.yml / heavy-timeout-nightly.yml — inject a test-only merged key set (prod + ci-2026a) via the new AIDOTNET_LICENSE_PUBLIC_KEY_JSON_TEST variable, so CI builds verify the CI token on the real AsymmetricLicenseVerifier path. release-please.yml is untouched → published NuGets trust production only.

Installed out-of-band (repo settings, not in this diff)

  • Generated the ci-2026a Ed25519 keypair; minted a scoped 30-day token (caps: model/tensors save+load, model:encrypt; no scope/machine binding). Signature verified against the CI public key.
  • Set repo variable AIDOTNET_LICENSE_PUBLIC_KEY_JSON_TEST = merged prod+ci JWK.
  • Set repo secret AIDOTNET_LICENSE_KEY = the aidn2 CI token.
  • The CI private key was used only to mint the token and was not persisted; rotate per the guide (new kid) when the token nears its 30-day expiry.

Generated with Claude Code

Documents the remaining operational half of the offline-license CI setup (the
csproj embedding + CI injection + AIDOTNET_LICENSE_KEY wiring already ship):
generating the CI Ed25519 keypair, minting a scoped short-exp CI token, and
installing the secret — including the trust-scope decision (keep the CI key out
of published packages vs. shipping it) and prod/CI key rotation steps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/franklins-projects-02a0b5a0?upgradeToPro=build-rate-limit

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: afba1adf-f36e-4d95-85d4-d6995f41b2d6

📥 Commits

Reviewing files that changed from the base of the PR and between d211380 and f42352b.

📒 Files selected for processing (5)
  • .github/workflows/heavy-timeout-nightly.yml
  • .github/workflows/sonarcloud.yml
  • tools/license-issuer/README.md
  • tools/license-issuer/aidn2_issuer.py
  • tools/license-issuer/keygen.py

Walkthrough

Adds documentation for the aidn2 offline Ed25519 license workflow, including token structure, CI key setup, GitHub secret installation, trust scope, and key rotation.

Changes

Offline License Documentation

Layer / File(s) Summary
License format and signing-key boundaries
tools/license-issuer/README.md
Documents the token grammar, Ed25519 signing semantics, claim fields, separate production and CI keys, and existing repository wiring.
CI key and bearer-token setup
tools/license-issuer/README.md
Provides procedures for generating CI keys, minting scoped short-lived tokens, and installing sealed bearer secrets through the GitHub Actions secrets API.
Trust scope and key rotation
tools/license-issuer/README.md
Describes CI public-key publication options and rotation procedures for CI and production signing keys.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

  • ooples/AiDotNet#1891: Adds the license-issuer scripts whose token and claims grammar is documented here.

Poem

Ed25519 keys softly gleam,
CI tokens sail through a sealed stream.
Claims take shape, rotations turn,
Trusted kids remain as licenses burn.
Offline paths now clearly learn.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and accurately reflects the main change: documenting offline CI license issuance with a test-only aidn2 key.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/ci-license-rotation

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/license-issuer/README.md`:
- Around line 15-17: Update the token grammar code fence in the README to
include an explicit language tag, preferably text, after the opening fence while
preserving the existing token format.
- Around line 49-68: Update the license-issuer README setup and rotation
instructions around keygen.py, aidn2_issuer.py, the JWK merge, and install
commands so every command is directly copy-pasteable. Document or use supported
output options to create ci_private.pem and ci_token.txt, replace literal
placeholder values with concrete paths or explicit copy steps, and ensure the
documented filenames match what the install and rotation scripts consume.
- Around line 112-115: Update the “Simpler, weaker — CI key ships” section to
remove the claim that the CI key is intrinsically scoped or short-lived. Either
remove this alternative entirely or explicitly state that embedding ci-2026a
grants full package trust to anyone holding its private key until emergency key
rotation, and that token claims and issuer policy alone provide scope and
expiration.
- Around line 87-96: The api function currently parses every response as JSON,
but the secret update PUT may return 204 No Content. Update api to inspect the
response status and return early for HTTP 204 before calling json.load, while
preserving JSON parsing for responses with content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e61dada1-4299-483c-aa97-40fbb8b1d296

📥 Commits

Reviewing files that changed from the base of the PR and between 4586e2c and d211380.

📒 Files selected for processing (1)
  • tools/license-issuer/README.md

Comment thread tools/license-issuer/README.md Outdated
Comment thread tools/license-issuer/README.md Outdated
Comment thread tools/license-issuer/README.md Outdated
Comment thread tools/license-issuer/README.md Outdated
…ffline path

Wires the CI test builds to embed a TEST-only public key set (prod + ci-2026a)
via the new AIDOTNET_LICENSE_PUBLIC_KEY_JSON_TEST variable, so the offline aidn2
CI token in the AIDOTNET_LICENSE_KEY secret verifies on the real
AsymmetricLicenseVerifier path (the ModuleInitializer keeps an offline-Active,
model:save-capable key; else it falls back to the synthetic license, so this is
non-breaking either way).

The CI key is deliberately kept OUT of AIDOTNET_LICENSE_PUBLIC_KEY_JSON (used by
release-please.yml), so published NuGets trust ONLY the production key —
a leaked CI key can never mint a license accepted by a shipped package.

- sonarcloud.yml: prefer the _TEST key set in the existing inject step.
- heavy-timeout-nightly.yml: add the inject step before build (it had none).

See tools/license-issuer/README.md for key generation + rotation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aidotnet_website Ready Ready Preview, Comment Jul 20, 2026 11:01pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
aidotnet-playground-api Ignored Ignored Preview Jul 20, 2026 11:01pm

@ooples ooples changed the title docs: aidn2 CI license key issuance + rotation guide ci+docs: real offline-licensed CI via a test-only aidn2 key (#15) Jul 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Title Auto-Fixed

Your PR title was automatically updated to follow Conventional Commits format.

Original title:
ci+docs: real offline-licensed CI via a test-only aidn2 key (#15)

New title:
ci: ci+docs: real offline-licensed CI via a test-only aidn2 key (#15)

Detected type: ci: (CI/workflow files changed)
Version impact: No release


Valid types and their effects:

  • feat: - New feature (MINOR bump: 0.1.0 → 0.2.0)
  • fix: - Bug fix (MINOR bump)
  • docs: - Documentation (MINOR bump)
  • refactor: - Code refactoring (MINOR bump)
  • perf: - Performance improvement (MINOR bump)
  • test: - Tests only (no release)
  • chore: - Build/tooling (no release)
  • ci: - CI/CD changes (no release)
  • style: - Code formatting (no release)
  • deps: - Dependency update (no release)

If the detected type is incorrect, you can manually edit the PR title.

@github-actions github-actions Bot changed the title ci+docs: real offline-licensed CI via a test-only aidn2 key (#15) ci: ci+docs: real offline-licensed CI via a test-only aidn2 key (#15) Jul 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Commit messages auto-fixed

One or more commit messages did not follow Conventional Commits, so they were rewritten to comply (subject case, header length ≤ 100, valid type). Each commit and its diff were preserved — no squashing.

The branch was force-pushed with the corrected messages. If you have local work on this branch, run git pull --rebase (or reset to the remote) before pushing again.

@ooples
ooples force-pushed the docs/ci-license-rotation branch from 0ce7101 to f42352b Compare July 20, 2026 22:59
@ooples
ooples merged commit f461e50 into master Jul 21, 2026
80 of 95 checks passed
@ooples
ooples deleted the docs/ci-license-rotation branch July 21, 2026 03:17
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.

2 participants