Files
git.stella-ops.org/docs/license-jwt-quota.md
2025-08-30 21:05:34 +00:00

4.5 KiB
Executable File
Raw Blame History

title, description, nav
title description nav
Offline JWT licence & dailyrun quota How StellaOps enforces a **runsperday** limit in fully airgapped deployments.
order
36

JWTbased dailyrun licence (offlinecapable)

When StellaOps scanners operate entirely offline, they cannot phone home for metering.
Instead, the backend accepts a signed JSON Web Token (JWT) that states the maximum number of scans per UTC day.
If no token is supplied, a grace quota of 33 runs/24h applies.


1  Token contents

Claim Purpose Example
sub Customer / licensee identifier "f47ac10b…"
iat Issuedat timestamp 1722566400
exp Absolute licence expiry 20251231T23:59:59Z
tier Max scans per UTC day {{ quota_token }}
tid Token identifier (32byte) "7d2285..."
pkg Product SKU / edition "stellacore"

Tokens are signed with RS256 and verified locally using the bundled public key. Only the public key ships inside the container; the private key never leaves the build pipeline.


2  Obtaining a token

  1. RequestPOST /register { email:"alice@example.org" }
  2. Service hashes the email (SHA256), stores it, and issues a JWT (60 days by default).
  3. Token is emailed to you.

A new request for the same email returns the same token until it nears expiry, avoiding quota “topups” by reregistration.


3  Supplying the token to an airgapped stack

# recommended
docker run \
  -v /opt/stella/license/alice.jwt:/run/secrets/stella_license.jwt:ro \
  stellaops

Other supported paths:

Method Mount point Hotreload
Docker secret /run/secrets/… ✓ (inotify)
Bindmounted userchosen path (above)
Env variable STELLA_LICENSE_JWT ✗ restart

4  Quotaenforcement algorithm

flowchart TD
    Start --> Verify[Verify JWT signature]
    Verify -->|Invalid| Deny1[Run in non licensed mode]
    Verify --> Load[load today's counter UTC]
    Load -->|SUM of last 24h scans < daily_quota| Permit[allow scan, add scan]
    Permit --> End
    Load -->|SUM of last 24h scans ≥ daily_quota| Deny1

5  Renewal procedure

Scenario Action
More capacity Request new token with higher daily_quota; replace file no restart needed
Licence expiry Same as above; new exp date
Key rotation Container image ships new public key(s); older tokens still verify

6  Fallback limits

Situation Daily quota
Valid JWT present value of daily_quota claim ({{ quota_token }})
No JWT 33
JWT expired (if used) treated as anonymous unless policy enforces hardfail
Token signature invalid 0 (reject)

7  Threatmodel highlights (future work / optional hardening)

Threat Mitigation
Copy token & DB to 2nd node Bind sub/tid to host fingerprint (TPM EK) optional enterprise control
Counter DB rollback Hashchain + monotonic clock optional enterprise control
Flooding single node Redisbacked cluster ratelimit (30 hits / 60s) + edge Nginx (20r/s)
Key compromise Rotate RS256 keypair, ship new pubkey, resign tokens

8  Anonymous (33 runs) mode

Offline PoCs without registration still work:

docker compose exec stella-ops stella-jwt reload   # reloads, discovers no token

…but production deployments must register to unlock realworld quotas and receive security advisories via email.


Last updated: 20250802