docs consolidation
This commit is contained in:
101
docs/modules/policy/POLICY_TEMPLATES.md
Executable file
101
docs/modules/policy/POLICY_TEMPLATES.md
Executable file
@@ -0,0 +1,101 @@
|
||||
# Policy Templates — YAML & Rego Examples
|
||||
|
||||
Stella Ops lets you enforce *pass / fail* rules in two ways:
|
||||
|
||||
1. **YAML “quick policies”** — simple equality / inequality checks.
|
||||
2. **OPA Rego modules** — full‑power logic for complex organisations.
|
||||
|
||||
> **Precedence:** If the same image is subject to both a YAML rule *and* a Rego
|
||||
> module, the **Rego result wins**. That is, `deny` in Rego overrides any
|
||||
> `allow` in YAML.
|
||||
|
||||
---
|
||||
|
||||
## 1 · YAML quick policy
|
||||
|
||||
```yaml
|
||||
# file: policies/root_user.yaml
|
||||
version: 1
|
||||
id: root-user
|
||||
description: Disallow images that run as root
|
||||
severity: high
|
||||
|
||||
rules:
|
||||
- field: ".config.user"
|
||||
operator: "equals"
|
||||
value: "root"
|
||||
deny_message: "Image runs as root — block."
|
||||
````
|
||||
|
||||
Place the file under `/opt/stella/plugins/policies/`.
|
||||
|
||||
---
|
||||
|
||||
## 2 · Rego example (deny on critical CVE)
|
||||
|
||||
```rego
|
||||
# file: policies/deny_critical.rego
|
||||
package stella.policy
|
||||
|
||||
default deny = []
|
||||
|
||||
deny[msg] {
|
||||
some f
|
||||
input.findings[f].severity == "critical"
|
||||
msg := sprintf("Critical CVE %s – build blocked", [input.findings[f].id])
|
||||
}
|
||||
```
|
||||
|
||||
*Input schema* — the Rego `input` document matches the public
|
||||
`ScanResult` POCO (see SDK). Use the bundled JSON schema in
|
||||
`share/schemas/scanresult.schema.json` for IDE autocompletion.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Pass‑through warnings (Rego)
|
||||
|
||||
Return a `warn` array to surface non‑blocking messages in the UI:
|
||||
|
||||
```rego
|
||||
package stella.policy
|
||||
|
||||
warn[msg] {
|
||||
input.image.base == "ubuntu:16.04"
|
||||
msg := "Image uses EOL Ubuntu 16.04 — please upgrade."
|
||||
}
|
||||
```
|
||||
|
||||
Warnings decrement the **quality score** but do *not* affect the CLI exit
|
||||
code.
|
||||
|
||||
---
|
||||
|
||||
## 4 · Testing policies locally
|
||||
|
||||
```bash
|
||||
# run policy evaluation without pushing to DB
|
||||
stella scan alpine:3.20 --policy-only \
|
||||
--policies ./policies/
|
||||
```
|
||||
|
||||
The CLI prints `PASS`, `WARN` or `DENY` plus structured JSON.
|
||||
|
||||
Unit‑test your Rego modules with the OPA binary:
|
||||
|
||||
```bash
|
||||
opa test policies/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5 · Developer quick‑start (plug‑ins)
|
||||
|
||||
Need logic beyond Rego? Implement a plug‑in via **C#/.NET {{ dotnet }}** and
|
||||
the `StellaOps.SDK` NuGet:
|
||||
|
||||
* Tutorial: [`dev/PLUGIN_DEV_GUIDE.md`](dev/PLUGIN_DEV_GUIDE.md)
|
||||
* Quick reference: `/plugins/`
|
||||
|
||||
---
|
||||
|
||||
*Last updated {{ "now" | date: "%Y‑%m‑%d" }} — constants auto‑injected.*
|
||||
@@ -16,7 +16,7 @@ This contract defines the integration between the Signals service (reachability
|
||||
|
||||
The canonical JSON schema is at:
|
||||
```
|
||||
docs/schemas/reachability-input.schema.json
|
||||
docs/modules/policy/schemas/reachability-input.schema.json
|
||||
```
|
||||
|
||||
## 3. Data Flow
|
||||
|
||||
93
docs/modules/policy/guides/QUOTA_ENFORCEMENT_FLOW.md
Executable file
93
docs/modules/policy/guides/QUOTA_ENFORCEMENT_FLOW.md
Executable file
@@ -0,0 +1,93 @@
|
||||
# Quota Enforcement — Flow Diagram (rev 2.1)
|
||||
|
||||
> **Scope** – this document explains *how* the free‑tier limits are enforced
|
||||
> inside the scanner service. For policy rationale and legal aspects, see
|
||||
> [`29_LEGAL_FAQ_QUOTA.md`](29_LEGAL_FAQ_QUOTA.md).
|
||||
|
||||
---
|
||||
|
||||
## 0 · Key parameters (rev 2.1)
|
||||
|
||||
| Symbol | Value | Meaning |
|
||||
|--------|-------|---------|
|
||||
| `L_anon` | **{{ quota_anon }}** | Daily ceiling for anonymous users |
|
||||
| `L_jwt` | **{{ quota_token }}** | Daily ceiling for token holders |
|
||||
| `T_warn` | `200` | Soft reminder threshold |
|
||||
| `D_soft` | `5 000 ms` | Delay for *first 30* over‑quota scans |
|
||||
| `D_hard` | `60 000 ms` | Delay for all scans beyond the soft window |
|
||||
|
||||
`L_active` is `L_jwt` if a valid token is present; else `L_anon`.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Sequence diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant API as Scanner API
|
||||
participant VALKEY as Valkey (quota)
|
||||
C->>API: /scan
|
||||
API->>VALKEY: INCR quota:<key>
|
||||
VALKEY-->>API: new_count
|
||||
alt new_count ≤ L_active
|
||||
API-->>C: 202 Accepted (no delay)
|
||||
else new_count ≤ L_active + 30
|
||||
API->>C: wait D_soft
|
||||
API-->>C: 202 Accepted
|
||||
else
|
||||
API->>C: wait D_hard
|
||||
API-->>C: 202 Accepted
|
||||
end
|
||||
````
|
||||
|
||||
*Counters auto‑expire **24 h** after first increment (00:00 UTC reset).*
|
||||
|
||||
---
|
||||
|
||||
## 2 · Valkey key layout
|
||||
|
||||
| Key pattern | TTL | Description |
|
||||
| ---------------------- | ---- | --------------------------------- |
|
||||
| `quota:ip:<sha256>` | 24 h | Anonymous quota per *hashed* IP |
|
||||
| `quota:tid:<sha256>` | 24 h | Token quota per *hashed* token‑ID |
|
||||
| `quota:ip:<sha256>:ts` | 24 h | First‑seen timestamp (ISO 8601) |
|
||||
|
||||
Keys share a common TTL for efficient mass expiry via `valkey-cli --scan`.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Pseudocode (Go‑style)
|
||||
|
||||
```go
|
||||
func gate(key string, limit int) (delay time.Duration) {
|
||||
cnt, _ := rdb.Incr(ctx, key).Result()
|
||||
|
||||
switch {
|
||||
case cnt <= limit:
|
||||
return 0 // under quota
|
||||
case cnt <= limit+30:
|
||||
return 5 * time.Second
|
||||
default:
|
||||
return 60 * time.Second
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*The middleware applies `time.Sleep(delay)` **before** processing the scan
|
||||
request; it never returns `HTTP 429` under the free tier.*
|
||||
|
||||
---
|
||||
|
||||
## 4 · Metrics & monitoring
|
||||
|
||||
| Metric | PromQL sample | Alert |
|
||||
| ------------------------------ | ------------------------------------------ | --------------------- |
|
||||
| `stella_quota_soft_hits_total` | `increase(...[5m]) > 50` | Many users near limit |
|
||||
| `stella_quota_hard_hits_total` | `rate(...[1h]) > 0.1` | Potential abuse |
|
||||
| Average delay per request | `histogram_quantile(0.95, sum(rate(...)))` | P95 < 1 s expected |
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Generated {{ "now" | date: "%Y‑%m‑%d" }} — values pulled from central constants.*
|
||||
120
docs/modules/policy/guides/QUOTA_OVERVIEW.md
Executable file
120
docs/modules/policy/guides/QUOTA_OVERVIEW.md
Executable file
@@ -0,0 +1,120 @@
|
||||
# Free‑Tier Quota — **{{ quota_anon }}/ {{ quota_token }} Scans per UTC Day**
|
||||
|
||||
Stella Ops is free for individual developers and small teams.
|
||||
To avoid registry abuse the scanner enforces a **two‑tier daily quota**
|
||||
— fully offline capable.
|
||||
|
||||
| Mode | Daily ceiling | How to obtain |
|
||||
|------|---------------|---------------|
|
||||
| **Anonymous** | **{{ quota_anon }} scans** | No registration. Works online or air‑gapped. |
|
||||
| **Free JWT token** | **{{ quota_token }} scans** | Email `token@stella-ops.org` (blank body). Bot replies with a signed JWT. |
|
||||
|
||||
*Soft reminder banner appears at 200 scans. Exceeding the limit never blocks –
|
||||
the CLI/UI introduce a delay, detailed below.*
|
||||
|
||||
---
|
||||
|
||||
## 1 · Token structure
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"iss": "stella-ops.org",
|
||||
"sub": "free-tier",
|
||||
"tid": "7d2285…", // 32‑byte random token‑ID
|
||||
"tier": {{ quota_token }}, // daily scans allowed
|
||||
"exp": 1767139199 // POSIX seconds (mandatory) – token expiry
|
||||
}
|
||||
````
|
||||
|
||||
* The **token‑ID (`tid`)** – not the e‑mail – is hashed *(SHA‑256 + salt)*
|
||||
and stored for counter lookup.
|
||||
* Verification uses the bundled public key (`keys/cosign.pub`) so **offline
|
||||
hosts validate tokens locally**. An optional `exp` claim may be present;
|
||||
if absent, the default is a far‑future timestamp used solely for schema
|
||||
compatibility.
|
||||
|
||||
---
|
||||
|
||||
## 2 · Enforcement algorithm (rev 2.1)
|
||||
|
||||
| Step | Operation | Typical latency |
|
||||
| ---- | ------------------------------------------------------------------------------ | ------------------------------------ |
|
||||
| 1 | `key = sha256(ip)` *or* `sha256(tid)` | < 0.1 ms |
|
||||
| 2 | `count = INCR quota:<key>` in Valkey (24 h TTL) | 0.2 ms (Lua) |
|
||||
| 3 | If `count > limit` → `WAIT delay_ms` | first 30 × 5 000 ms → then 60 000 ms |
|
||||
| 4 | Return HTTP 429 **only if** `delay > 60 s` (should never fire under free tier) | — |
|
||||
|
||||
*Counters reset at **00:00 UTC**.*
|
||||
|
||||
---
|
||||
|
||||
## 3 · CLI / API integration
|
||||
|
||||
```bash
|
||||
# Example .env
|
||||
docker run --rm \
|
||||
-e DOCKER_HOST="$DOCKER_HOST" \ # remote‑daemon pointer
|
||||
-v "$WORKSPACE/${SBOM_FILE}:/${SBOM_FILE}:ro" \ # mount SBOM under same name at container root
|
||||
-e STELLA_OPS_URL="https://${STELLA_URL}" \ # where the CLI posts findings
|
||||
"$STELLA_URL/registry/stella-cli:latest" \
|
||||
scan --sbom "/${SBOM_FILE}" "$IMAGE"
|
||||
```
|
||||
|
||||
*No JWT? → scanner defaults to anonymous quota.*
|
||||
|
||||
---
|
||||
|
||||
## 4 · Data retention & privacy
|
||||
|
||||
| Data | Retention | Purpose |
|
||||
| ---------------------- | ------------------------------------ | ---------------- |
|
||||
| IP hash (`quota:ip:*`) | 7 days, then salted hash only | Abuse rate‑limit |
|
||||
| Token‑ID hash | Until revoked | Counter lookup |
|
||||
| E‑mail (token request) | ≤ 7 days unless newsletters opted‑in | Deliver the JWT |
|
||||
|
||||
*No personal data leaves your infrastructure when running offline.*
|
||||
|
||||
---
|
||||
|
||||
## 5 · Common questions
|
||||
|
||||
<details>
|
||||
<summary>What happens at exactly 200 scans?</summary>
|
||||
|
||||
> The UI/CLI shows a yellow “fair‑use reminder”.
|
||||
> No throttling is applied yet.
|
||||
> Once you cross the full limit, the **first 30** over‑quota scans incur a
|
||||
> 5‑second delay; further excess scans delay **60 s** each.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Does the quota differ offline?</summary>
|
||||
|
||||
> No. Counters are evaluated locally in Valkey; the same limits apply even
|
||||
> without Internet access.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can I reset counters manually?</summary>
|
||||
|
||||
> Yes – delete the `quota:*` keys in Valkey, but we recommend letting them
|
||||
> expire at midnight to keep statistics meaningful.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 6 · Revision history
|
||||
|
||||
| Version | Date | Notes |
|
||||
| ------- | ---------- | ------------------------------------------------------------------- |
|
||||
| **2.1** | 2025‑07‑16 | Consolidated into single source; delays re‑tuned (30 × 5 s → 60 s). |
|
||||
| 2.0 | 2025‑04‑07 | Switched from MongoDB (removed Sprint 4400) to Valkey (Redis-compatible) for quota counters. |
|
||||
| 1.0 | 2024‑12‑20 | Initial free‑tier design. |
|
||||
|
||||
---
|
||||
|
||||
**Authoritative source** — any doc or website section that references quotas
|
||||
*must* link to this file instead of duplicating text.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Status:** Implementation in Progress (SPRINT_3000_0100_0001)
|
||||
> **Predicate URI:** `https://stellaops.dev/predicates/policy-verdict@v1`
|
||||
> **Schema:** [`docs/schemas/stellaops-policy-verdict.v1.schema.json`](../schemas/stellaops-policy-verdict.v1.schema.json)
|
||||
> **Schema:** [`docs/modules/policy/schemas/stellaops-policy-verdict.v1.schema.json`](../schemas/stellaops-policy-verdict.v1.schema.json)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user