4.5 KiB
Executable File
title, description, nav
| title | description | nav | ||
|---|---|---|---|---|
| Offline JWT licence & daily‑run quota | How Stella‑Ops enforces a **runs‑per‑day** limit in fully air‑gapped deployments. |
|
JWT‑based daily‑run licence (offline‑capable)
When Stella‑Ops 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/24 h applies.
1 Token contents
| Claim | Purpose | Example |
|---|---|---|
sub |
Customer / licensee identifier | "f47ac10b…" |
iat |
Issued‑at timestamp | 1722566400 |
exp |
Absolute licence expiry | 2025‑12‑31T23:59:59Z |
tier |
Max scans per UTC day | {{ quota_token }} |
tid |
Token identifier (32‑byte) | "7d2285..." |
pkg |
Product SKU / edition | "stella‑core" |
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
- Request →
POST /register { email:"alice@example.org" } - Service hashes the e‑mail (SHA‑256), stores it, and issues a JWT (60 days by default).
- Token is e‑mailed to you.
A new request for the same e‑mail returns the same token until it nears expiry, avoiding quota “top‑ups” by re‑registration.
3 Supplying the token to an air‑gapped stack
# recommended
docker run \
-v /opt/stella/license/alice.jwt:/run/secrets/stella_license.jwt:ro \
stella‑ops
Other supported paths:
| Method | Mount point | Hot‑reload |
|---|---|---|
| Docker secret | /run/secrets/… |
✓ (inotify) |
| Bind‑mounted | user‑chosen path (above) | ✓ |
| Env variable | STELLA_LICENSE_JWT |
✗ restart |
4 Quota‑enforcement 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 hard‑fail |
| Token signature invalid | 0 (reject) |
7 Threat‑model 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 | Hash‑chain + monotonic clock – optional enterprise control |
| Flooding single node | Redis‑backed cluster rate‑limit (30 hits / 60 s) + edge Nginx (20 r/s) |
| Key compromise | Rotate RS256 key‑pair, ship new pubkey, re‑sign 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 real‑world quotas and receive security advisories via e‑mail.
Last updated: 2025‑08‑02