Initial commit

This commit is contained in:
2025-08-30 21:05:34 +00:00
commit b04557a923
40 changed files with 5469 additions and 0 deletions

123
docs/license-jwt-quota.md Executable file
View File

@@ -0,0 +1,123 @@
---
title: Offline JWT licence & dailyrun quota
description: How StellaOps enforces a **runsperday** limit in fully airgapped deployments.
nav:
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. **Request**`POST /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
```bash
# 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
```mermaid
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:
```bash
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*