feat: license handshake and x-descope-license header#730
Open
orius123 wants to merge 3 commits into
Open
Conversation
Adds a management.license.get() endpoint that calls /v1/mgmt/license and returns the rate limit tier. The SDK fires the request once on init (when a managementKey is configured) and injects the returned tier value in the x-descope-license header on every subsequent management request so Cloudflare can apply the correct rate limit bucket per customer tier. Tier values: tier1 (free), tier2 (pro), tier3 (growth), tier4 (enterprise). Ref: descope/etc#14245
Fire-and-forget handshake and the rate-limit-tier header injection are best-effort defensive code paths. The license endpoint client itself remains fully covered by license.test.ts; the end-to-end handshake will be exercised by integration tests.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a license-tier handshake to the management SDK so Cloudflare can apply per-customer rate-limit buckets. When a managementKey is configured, the SDK asynchronously fetches the customer's tier on initialization and injects it into every subsequent management request as the x-descope-license header.
Changes:
- New
management.license.get()endpoint (GET /v1/mgmt/license) andLicensetype withrateLimitTier. - On SDK init, fires a one-shot license fetch and caches
rateLimitTier; failure logs atdebugand is non-fatal. - Management
beforeRequesthook injectsx-descope-license: <tier>once cached.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/management/types.ts | Adds License type. |
| lib/management/paths.ts | Adds /v1/mgmt/license path entry. |
| lib/management/license.ts | New wrapper exposing get() for the license endpoint. |
| lib/management/license.test.ts | Unit test for the new get() endpoint. |
| lib/management/index.ts | Wires withLicense into the management surface. |
| lib/index.ts | Adds tier cache, fire-and-forget handshake, and header injection in beforeRequest. |
Comments suppressed due to low confidence (1)
lib/index.ts:174
- The
.catchhandler swallows handshake errors with only alogger?.debug?.call. Ifloggeris not provided, or itsdebuglevel is filtered out (which is common in production logging configurations), the failure becomes completely invisible — operators have no signal that the handshake is failing and the tier header is missing. Consider usinglogger?.warn(or at leastlogger?.info) for handshake failures since a missing tier header will cause Cloudflare to rate-limit the customer at the default/lowest bucket, which has real customer-visible consequences.
.catch((e) => {
logger?.debug?.('License handshake failed', e);
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+139
to
+175
| @@ -144,6 +157,23 @@ const nodeSdk = ({ | |||
| headers: nodeHeaders, | |||
| }); | |||
|
|
|||
| // Fire-and-forget license handshake. Backend skips license-header validation | |||
| // for the GetLicense endpoint itself, so this initial request is safe even | |||
| // before the tier is cached. | |||
| /* istanbul ignore next */ | |||
| if (managementKey) { | |||
| management.license | |||
| .get() | |||
| .then((resp) => { | |||
| if (resp.ok && resp.data?.rateLimitTier) { | |||
| rateLimitTier = resp.data.rateLimitTier; | |||
| } | |||
| }) | |||
| .catch((e) => { | |||
| logger?.debug?.('License handshake failed', e); | |||
| }); | |||
| } | |||
Comment on lines
+160
to
+175
| // Fire-and-forget license handshake. Backend skips license-header validation | ||
| // for the GetLicense endpoint itself, so this initial request is safe even | ||
| // before the tier is cached. | ||
| /* istanbul ignore next */ | ||
| if (managementKey) { | ||
| management.license | ||
| .get() | ||
| .then((resp) => { | ||
| if (resp.ok && resp.data?.rateLimitTier) { | ||
| rateLimitTier = resp.data.rateLimitTier; | ||
| } | ||
| }) | ||
| .catch((e) => { | ||
| logger?.debug?.('License handshake failed', e); | ||
| }); | ||
| } |
- Warn (not debug) on handshake failure so operators see when the tier header is missing - Remove istanbul ignore directives and cover the handshake paths with unit tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds license handshake support so Cloudflare can apply the correct rate-limit bucket per customer tier.
management.license.get()(GET/v1/mgmt/license) returning{ rateLimitTier: string }managementKeyis configured, the SDK fires a one-shot license fetch in the background and cachesrateLimitTierx-descope-license: <tier>via the existingbeforeRequesthookTier values
tier1(free) /tier2(pro) /tier3(growth) /tier4(enterprise)Notes
logger?.debugand the SDK continues without the headerGetLicenseitself, so the initial fetch is safe before the tier is cachedTest plan
lib/management/license.test.tscovers the new endpointRef: descope/etc#14245