Skip to content

feat(shared): decouple offline anonymous access from seat cap#1349

Merged
brendan-kellam merged 3 commits into
mainfrom
brendan/decouple-offline-anonymous-access
Jun 18, 2026
Merged

feat(shared): decouple offline anonymous access from seat cap#1349
brendan-kellam merged 3 commits into
mainfrom
brendan/decouple-offline-anonymous-access

Conversation

@brendan-kellam

@brendan-kellam brendan-kellam commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Overview

Offline-license anonymous access was derived from whether seats was set: isAnonymousAccessAvailable returned true only when seats === undefined. That conflated two unrelated concepts — uncapped seats and anonymous access allowed — so the only way to issue an uncapped offline license also turned on anonymous access.

This decouples them with an explicit, signed anonymousAccess field on the offline license payload.

Changes

  • offlineLicensePayloadSchema gains anonymousAccess: z.boolean().optional().
  • The field is added to the signed dataToVerify, listed alphabetically to match the signer's canonical (sort_keys=True) JSON. Because JSON.stringify drops undefined, legacy keys without the field verify byte-for-byte as before — no signature breakage.
  • isAnonymousAccessAvailable now returns offlineKey.anonymousAccess === true instead of seats === undefined.
  • OfflineLicenseMetadata / getOfflineLicenseMetadata expose the new field.
  • getSeatCap is unchanged: uncapped is still "omit seats", now without the anonymous-access side effect.
  • Tests updated to cover the decoupled behavior.

⚠️ Behavior change

Any existing uncapped offline license that relied on seats === undefined for anonymous access will lose anonymous access after this change and must be re-issued with anonymousAccess: true. This is inherent to the decoupling.

Related

Requires the matching license-generator change in sb-license-key-utils (adds a --anonymous-access flag). Old keys are unaffected, so rollout ordering is low-risk.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Changed
    • Offline license anonymous access is now controlled by an explicit setting in the license key, rather than being inferred from seat/cap configuration.
    • Anonymous-access offline keys can override an active online license, and when the offline key is expired, evaluation falls back to online license behavior.
  • Tests
    • Updated and expanded validation to match the new anonymous-access rules and precedence behavior.

Offline license anonymous access was derived from `seats === undefined`,
which conflated "uncapped seats" with "anonymous access allowed" — an
uncapped license inadvertently enabled anonymous access.

Add an explicit, signed `anonymousAccess` boolean to the offline license
payload and drive `isAnonymousAccessAvailable` from it, so seat-capping and
anonymous access can be set independently. Uncapped licenses no longer grant
anonymous access unless `anonymousAccess: true` is set.

The new field is included in the signed payload alphabetically; since
`JSON.stringify` drops `undefined`, legacy keys without the field verify
byte-for-byte as before.

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

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ae80c5e-9b56-4eba-8099-218a1488c69e

📥 Commits

Reviewing files that changed from the base of the PR and between 3c47dad and 9426727.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • packages/shared/src/entitlements.ts
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/shared/src/entitlements.ts

Walkthrough

Offline license payloads gain an explicit anonymousAccess?: boolean field. The signature verification canonical JSON is updated to include it, isAnonymousAccessAvailable now returns true only when that field is explicitly true (replacing seat-count inference), OfflineLicenseMetadata exposes the field, tests are revised accordingly, and a changelog entry records the change.

Changes

Offline license anonymousAccess field

Layer / File(s) Summary
Schema, signature verification, and metadata type
packages/shared/src/entitlements.ts
Adds anonymousAccess?: boolean to the offline license zod schema; includes it in the canonical JSON string used for signature verification (with undefined-omission compatibility for legacy payloads); extends OfflineLicenseMetadata type and getOfflineLicenseMetadata return value with the new field.
isAnonymousAccessAvailable logic change and changelog
packages/shared/src/entitlements.ts, CHANGELOG.md
Replaces the seat-count-absence inference with an explicit anonymousAccess === true check in isAnonymousAccessAvailable; adds a changelog entry documenting this behavioral change.
Test helper and test case updates
packages/shared/src/entitlements.test.ts
Extends validOfflineKey to accept and encode anonymousAccess; rewrites offline-key tests covering: no default grant without flag, seat-cap independence, override of active online license, expired-key fallthrough to online evaluation, and invalid-signature fallthrough.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • sourcebot-dev/sourcebot#335: Main PR extends the offline license payload/signature verification to include an explicit anonymousAccess field in the canonical dataToVerify, which is the same kind of signature-verification logic modified by retrieved PR #335 (adding/using signed sig payload verification for license keys).
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(shared): decouple offline anonymous access from seat cap' directly and clearly summarizes the main change—decoupling anonymous access from seat cap configuration. It matches the PR objectives and accurately reflects the primary intent of the changeset.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brendan/decouple-offline-anonymous-access

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

@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: 1

🤖 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 `@CHANGELOG.md`:
- Line 11: The CHANGELOG.md entry at line 11 contains two separate sentences
when it should be a single sentence per the coding guidelines. Combine the two
sentences describing the decoupling of offline-license anonymous access and the
introduction of the explicit anonymousAccess field into one cohesive sentence.
Keep the [EE] prefix at the beginning and maintain the PR link format
[`#1349`](https://github.com/sourcebot-dev/sourcebot/pull/1349) at the end of the
entry.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bed8b16-7ead-42bb-ab99-679f007a0c75

📥 Commits

Reviewing files that changed from the base of the PR and between 9320065 and 3c47dad.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • packages/shared/src/entitlements.test.ts
  • packages/shared/src/entitlements.ts

Comment thread CHANGELOG.md Outdated
msukkari
msukkari previously approved these changes Jun 18, 2026
@brendan-kellam brendan-kellam merged commit ece54b2 into main Jun 18, 2026
8 checks passed
@brendan-kellam brendan-kellam deleted the brendan/decouple-offline-anonymous-access branch June 18, 2026 21:43
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