License Operator Overview
The License Operator is the licensing and billing brain of the Authlance platform.
It manages license issuance, coordinates Stripe-powered purchases, publishes JetStream events for downstream provisioning, and exposes REST APIs suitable for storefronts, back-office dashboards, and automation scripts.
It ships with both a long-running server and a CLI for headless operations.
Core Capabilities
-
License issuance
Generates signed RSA license envelopes for trials, manual grants, and subscription purchases. Licenses may be persisted or returned on demand. -
Stripe automation
Creates hosted checkout sessions, listens tocheckout.session.completedandinvoice.payment_succeededevents, and issues licenses based on Stripe metadata.
Non-subscription flows are supported through coupons, quotas, and multi-year terms. -
Product catalog
Public endpoints (/authlance/license/productand/authlance/license/products) expose storefront-ready metadata including features, coupons, and pricing tiers. -
Internal admin APIs
Authenticated endpoints allow issuing trials, retrieving group licenses, exporting transaction history, verifying payments, and downloading stored artifacts. -
Event emission
Stripe-hosted purchases emit alicenseoperator.license.issuedNATS JetStream message (triggered from the webhook handler) that carries the signed payload plus Stripe identifiers. Manual API or CLI issuance stays silent, so downstream consumers should only expect events for paid purchases. -
CLI workflows
Thelicenseoperatorbinary supports:serve– run the serverlicense issue– direct/manual issuancestripe ensure-webhook– create/update webhooksstripe sync-products– seed Stripe with product/price definitions
Architecture at a Glance
[ Dashboard / Storefront ]
|
v
[ License Operator ] <---> [ Stripe API / Webhooks ]
|
v
[ MySQL ] [ NATS JetStream ]
|
v
[ Email / Provisioning ]
Typical Request Flows
1. Self-Service Purchase
- Dashboard calls
POST/authlance/license/payments/api/v1/checkout-session - Customer completes payment on Stripe’s hosted checkout
- Stripe fires webhook to
POST/authlance/license/stripe/webhook - The webhook handler:
- Validates signature
- Enforces
group_namerequirements - Issues the license
- Persists it
- Emits the
licenseoperator.license.issuedevent (webhook issuances only)
2. Manual Issuance (Admin or Automation)
- API: POST
/authlance/license/internal/issue - CLI:
licenseoperator license issue \ --group acme \ --email owner@acme.com \ --plan alpha \ --product-key auth \ --expires 2026-01-01T00:00:00Z \ --persist
3. Trial Management
POST /authlance/license/trial/{group}/issue:
- Enforces one trial per group
- Applies cooldown logic
- Uses default trial length unless explicitly overridden
Operational Requirements
Configuration
All configuration lives in config/config.yaml and can be overridden by environment variables (LICENSEOPERATOR_*).
You must provide:
- Server bind address
- MySQL connection string
- Stripe API key + webhook secret
- At least one RSA signing key pair
- NATS JetStream connectivity
- JWT secret for internal API scopes
Secrets to manage
- RSA private keys (
PEM) - Stripe secret key & webhook secret
- MySQL DSN
- JWT secret for internal admin APIs
External Systems Required
- Stripe (billing and webhooks)
- MySQL 8+
- NATS JetStream cluster
Command Reference
Run the server
licenseoperator serve --config /app/config/config.yaml
Issue a license
licenseoperator license issue \
--group acme \
--email owner@acme.com \
--plan alpha \
--product-key auth \
--expires 2026-01-01T00:00:00Z \
--persist
Ensure Stripe webhook
licenseoperator stripe ensure-webhook \
--config /app/config/config.yaml \
--public-url https://licenseoperator.example.com
Sync product catalog to Stripe
licenseoperator stripe sync-products --config /app/config/config.yaml
All commands accept --config and honor LICENSEOPERATOR_ environment variables.
Admin & Payments APIs
The License Operator exposes two protected API surfaces in addition to the public product catalog and Stripe webhook. Both require valid dashboard-issued JWTs (internal_auth configuration) and the server’s own license must be valid for the admin routes to respond.
Product & Coupon Administration
/authlance/license/admin/products (and nested routes) allow authenticated sysadmins to manage catalog metadata without editing configuration files:
GET /authlance/license/admin/products– paginated list with optional inclusion of inactive/internal SKUs (requiressysadminrole).POST /authlance/license/admin/products– create a product definition, optionally with coupons or pricing tiers.PUT /authlance/license/admin/products/{slug}– update product settings and seat caps.GET /authlance/license/admin/products/seat-usage– report current seat/coupon consumption to reconcile limits./authlance/license/admin/products/{slug}/coupons– list/create/update/delete coupon overrides tied to a product./authlance/license/product-keys– enumerate configured signing keys to help dashboard flows map SKUs to keys.
These endpoints honor the same limits documented in the configuration section (for example max_managed_products or max_license_total) and will refuse mutations if the operator’s license guard reports an invalid status.
Payments & Reporting
Routes rooted at /authlance/license/payments power checkout orchestration and Stripe reporting:
POST /authlance/license/payments/product-details– public helper that returns Stripe price data by lookup key for storefronts.POST /authlance/license/payments/api/v1/checkout-session– authenticated creation of hosted checkout sessions; enforces coupon rules,managed_productslimits, and the configured default slugs.POST /authlance/license/payments/api/v1/customer-portal– Spins up a Stripe billing-portal session for the authenticated customer.POST /authlance/license/payments/api/v1/session-id– Exchanges a checkout session ID for the resulting subscription ID.GET /authlance/license/payments/api/v1/reports/payments– Paginated payment history filtered by date range, product, or organization.GET /authlance/license/payments/api/v1/reports/payments/export– CSV export of the same data.GET /authlance/license/payments/api/v1/verify-payment/{licenseId}(and/licenseoperator/verify-payment/{licenseId}) – Verifies that a license has a matching Stripe payment record for audit purposes.
All routes under /api/v1 require the JWT scopes configured via internal_auth, and the reporting endpoints depend on the Stripe payment persistence described in the README.
License Limits and Seat Enforcement
For the license operator, a "seat" represents the number of active Stripe products a customer is entitled to manage.
Each synced, active Stripe product consumes exactly one seat.
How seats behave:
- Disabling a product in Stripe frees a seat
- Adding a product consumes a seat immediately
- Purchases stamp the seat count onto the signed license
What the License Operator backend enforces
1. Checkout validation
- Requires a non-negative seat count
- Enforces product-level maxima via
max_managed_products - Enforces coupon-level minima/maxima
- Stores the final value in Stripe metadata (
managed_products)
2. License issuance
- Copies seat count into the signed payload (
Seats) - Ensures all persisted records match what the customer purchased
3. Active-product enforcement (downstream — your software must handle this)
The License Operator does not enforce live seat usage.
It only validates limits at purchase time and embeds the purchased seat count into the signed license envelope.
Your application is responsible for enforcing seats at runtime.
This means that your product, dashboard, or custom storefront must:
- Count how many of your own seats/features/modules are currently active for the customer
- Compare that number with the
seatsvalue found inside the signed license - Prevent the customer from activating additional products/features once they reach that limit
If you do not implement this check, customers will be able to exceed their purchased entitlements.
In other words:
The License Operator guarantees correct licensing at purchase time;
your application must enforce those limits at runtime.
Runtime enforcement usually happens inside:
- Your Auth container
- Your feature modules
- Your admin dashboard
- Any provisioning or activation logic that turns on a product/feature for a tenant
4. Total license caps
Products with max_license_total enforce:
- Issuance caps for one-off/perpetual SKUs
- Prevention of overselling exclusive packages
Guarantees
With these rules:
- Customers cannot exceed the maximum seat count per SKU
- Coupons cannot grant unauthorized higher tiers
- Every signed license contains an immutable seat count
- Downstream services enforce entitlements consistently
Who Benefits from a License Operator License
- Teams selling multiple Authlance-based SKUs who need unified billing + licensing
- Revenue ops teams needing Stripe-driven entitlement enforcement
- SaaS vendors entering marketplace/partner ecosystems where product caps matter
- Early-stage platforms without an internal entitlement service but requiring cryptographically signed licenses