Skip to content

feat: license handshake and x-descope-license header#1537

Open
orius123 wants to merge 2 commits into
mainfrom
feat/rate-limit-tier
Open

feat: license handshake and x-descope-license header#1537
orius123 wants to merge 2 commits into
mainfrom
feat/rate-limit-tier

Conversation

@orius123
Copy link
Copy Markdown
Member

@orius123 orius123 commented May 14, 2026

Summary

Adds license handshake support so Cloudflare can apply the correct rate-limit bucket per customer tier.

  • Adds mgmt.license.get() (GET /v1/mgmt/license) returning { rateLimitTier: str }
  • On client init (when a management key is configured), the SDK runs a synchronous handshake with a 5s timeout
  • The cached tier is injected into the x-descope-license header on every management request

Tier values

tier1 (free) / tier2 (pro) / tier3 (growth) / tier4 (enterprise)

Notes

  • Handshake failure is non-fatal: a warning is logged via the standard logging module and the SDK runs without the header
  • The backend interceptor skips license-header validation for GetLicense itself, so the initial fetch is safe before the tier is cached
  • Matches the Go SDK pattern (sync fetch on construction) so short-lived processes (CLI, cron, serverless cold starts) carry the header on their first request

Test plan

  • tests/management/test_license.py covers the new endpoint and the header injection logic
  • Integration tests will exercise the end-to-end handshake + header injection

Ref: descope/etc#14245

Adds a mgmt.license.get() endpoint that calls /v1/mgmt/license and
returns the rate limit tier. On client init (when a management key is
configured), the SDK runs a fire-and-forget handshake in a daemon
thread to cache the tier on the HTTP client. Subsequent management
requests carry the cached value in the x-descope-license header so
Cloudflare can apply the correct rate limit bucket per customer tier.

Tier values: tier1 (free), tier2 (pro), tier3 (growth), tier4 (enterprise).

Handshake failure is non-fatal, the SDK continues without the header.
The backend interceptor skips license-header validation for the
GetLicense endpoint itself, so the initial fetch is safe before the
tier is cached.

Ref: descope/etc#14245
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Coverage report

The coverage rate went from 98.37% to 98.29% ⬇️

91.17% of new lines are covered.

Diff Coverage details (click to unfold)

descope/mgmt.py

100% of new lines are covered (100% of the complete file).

descope/descope_client.py

82.35% of new lines are covered (96.19% of the complete file).
Missing lines: 141, 142, 143

descope/management/common.py

100% of new lines are covered (99.73% of the complete file).

descope/http_client.py

100% of new lines are covered (100% of the complete file).

descope/management/license.py

100% of new lines are covered (100% of the complete file).

@orius123 orius123 marked this pull request as ready for review May 14, 2026 15:39
Copilot AI review requested due to automatic review settings May 14, 2026 15:39
@orius123 orius123 requested review from LioriE and omercnet May 14, 2026 15:40
@orius123 orius123 enabled auto-merge (squash) May 14, 2026 15:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds management license support and a client-side handshake to cache the project’s rate-limit tier for management requests.

Changes:

  • Adds mgmt.license.get() for GET /v1/mgmt/license.
  • Starts a background license handshake when a management key is configured.
  • Injects cached tiers into the x-descope-license management request header.

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
descope/descope_client.py Starts the background license handshake and caches the tier.
descope/http_client.py Adds the cached tier to default request headers.
descope/mgmt.py Exposes the new mgmt.license facade.
descope/management/common.py Adds the license endpoint path constant.
descope/management/license.py Implements the management license API wrapper.
tests/management/test_license.py Adds tests for license retrieval and header behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread descope/descope_client.py Outdated
Comment thread descope/descope_client.py Outdated
LioriE
LioriE previously approved these changes May 14, 2026
Comment thread descope/descope_client.py Outdated
if tier:
self._mgmt_http_client.rate_limit_tier = tier
except Exception:
# Handshake failure is non-fatal, SDK continues without the header.
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.

@orius123 making sure its ok

Replace the daemon-thread handshake with a synchronous httpx.get on
construction, bounded by a 5s timeout. The async fetch left short-lived
processes (CLI, cron, serverless cold starts) running without the
x-descope-license header, landing them in Cloudflare's default rate
limit bucket. The change matches the Go SDK pattern.

Replace the silent except with logger.warning so handshake failures are
operator-visible.
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.

3 participants