Add initial documentation

This commit is contained in:
2025-07-20 21:38:21 +03:00
parent 8ba0a0ba6d
commit 42d9d2d860
24 changed files with 4447 additions and 0 deletions

View File

@ -0,0 +1,34 @@
# 📚 StellaOps Documentation TableofContents
1. **Introductory Guides**
- [WhatIsIt](01_WHAT_IS_IT.md)
- [Why](02_WHY.md)
- [ProductVision](03_VISION.md)
2. **Specifications**
- [System Requirements Spec](05_SYSTEM_REQUIREMENTS_SPEC.md)
- [Feature Matrix](04_FEATURE_MATRIX.md)
- [HighLevel Architecture](07_HIGH_LEVEL_ARCHITECTURE.md)
- [Module Specifications](08_MODULE_SPECIFICATIONS.md)
- [API & CLI Reference](09_API_CLI_REFERENCE.md)
- [Plugin SDK Guide](10_PLUGIN_SDK_GUIDE.md)
- [Data Schemas](11_DATA_SCHEMAS.md)
3. **Performance & Quality**
- [Performance Workbook](12_PERFORMANCE_WORKBOOK.md)
- [Coding standards](18_CODING_STANDARDS.md)
- [Test suite](19_TEST_SUITE_OVERVIEW.md)
- [Legal faq uota](29_LEGAL_FAQ_QUOTA.md)
4. **User Interface**
- [UI Guide](15_UI_GUIDE.md)
5. **Operational HowTo**
- [Recipes & Pipelines](22_RECIPES_PIPELINES_LIBRARY.md)
- [Offline/OUK Admin Guide](24_OUK_ADMIN_GUIDE.md)
6. **Support**
- [FAQ Matrix](23_FAQ_MATRIX.md)
7. **Glossary of Terms**
- [Glossary of Terms](14_GLOSSARY_OF_TERMS.md)

77
docs/01_WHAT_IS_IT.md Normal file
View File

@ -0,0 +1,77 @@
# 1 · WhatIs- **StellaOps**?
StellaOps is a **selfhosted, SBOMfirst DevSecOps platform** that gives engineering and security teams instant (<5s) feedback on container and artifact riskeven when they run completely offline.
It is built around five design pillars: **modular, open, fast, local, and UIcontrollable**.
---
## 1. What the Product Does  7Point Snapshot
| # | Capability | What It Means in Practice |
|---|------------|---------------------------|
| **1** | **SBOMCentric Scanning** | Generates and scans *SoftwareBills of Materials* (Trivy JSON, SPDXJSON, CycloneDXJSON); autodetects format and stores each SBOM as a blob. |
| **2** | **DeltaSBOM Engine** | Uploads SBOM only for *new* layers; warmcache image rescans complete in <1s. |
| **3** | **Anonymous Internal Registry** | Ships a builtin `StellaOps.Registry` so agents (`SanTech`, `Zastava`, SBOMbuilder) can be pulled inside airgapped networks without external credentials. |
| **4** | **PolicyasCode** | Supports YAML rules today and OPA/Rego (`StellaOps.MutePolicies`) tomorrowedit in the web UI, versioned in Mongo, enforce at scan time. |
| **5** | **Pluggable Modules** | Every scanner, exporter, or attestor is a hotload .NET plugin (e.g., `StellaOpsAttestor` for SLSA/Rekor in the roadmap). |
| **6** | **Horizontally Scalable** | Stateless API backed by Redis & Mongo; optional Kubernetes charts for multinode performance. |
| **7** | **Sovereign & Localised** | Russianlanguage UI, local vulnerability DB mirrors, and no telemetry by defaultready for ГОСТ/FSTECsensitive deployments. |
> **🆓 Free tier update (July2025)** Every selfhosted instance now includes **333 scans per UTC day**.
> A yellow banner appears once you cross **200 scans** (≈60% of quota).
> Past 333, `/scan` responds with soft 5s waits for 30 calls, then a hard **60s waitwall** until the daily reset.
---
## 2. How It Works  EndtoEnd Flow (30sec tour)
1. **Build Phase**
`sbombuilder` container runs inside CI, pulls base layers metadata, and queries `/layers/missing`receiving in ~20ms which layers still need SBOMs.
New layers SBOM generated `*.sbom.<type>` + `*.sbom.type` dropped next to image tarball.
2. **Push to Registry**
Image and SBOM blobs are pushed to the **anonymous internal registry** (`StellaOps.Registry`). Cosign tags are attached if enabled.
3. **Scan Phase**
`SanTech` agent pulls the SBOM blob, sends `/scan?sbomType=spdx-json` to backend. If flag is absent, backend autodetects.
Freetier tokens inherit the **333scan/day quota**; response headers expose remaining scans and reset time.
4. **Policy & Risk Evaluation**
Backend hydrates CVE data, merges any cached layer scores, and calls the **PolicyasCode engine**:
* YAML rules builtin interpreter;
* Rego policies (future) embedded OPA.
5. **Attestation & Transparency** *(Roadmap)*
`StellaOpsAttestor` signs results with SLSA provenance and records them in a local **Rekor** mirror for tamperproof history.
6. **Feedback Loop**
CLI exits with nonzero on policy block.
UI dashboard shows findings, quota banner, and pertoken scan counters; triagers can mute or set expiry dates directly.
---
## 3. Why Such a Product Is Needed
> *“Software supplychain attacks have increased **742%** over the past three years.”*  Sonatype 2024 State of the Software Supply Chain
### Key Drivers & Regulations
| Driver | Detail & Obligation |
|--------|--------------------|
| **Government SBOM Mandates** | **USEO14028** & NISTSP800218 require suppliers to provide SBOMs.<br>• EU **Cyber Resilience Act (CRA)** will demand attestations of secure development by 2026. |
| **SLSA & SSDF Frameworks** | Industry pushes toward **SLSA v1.0** levels 23 and NIST **SSDF1.1** controls, emphasising provenance and policy enforcement. |
| **Transparency Logs** | **Sigstore Rekor** gains traction as a standard for tamperevident signatures—even for airgapped replicas. |
| **Offline & Sovereign Deployments** | Criticalinfra operators (finance, telecom, defence) must run security tooling without Internet and with local language/VDB support. |
| **Performance Expectations** | Modern CI/CD pipelines trigger hundreds of image builds daily; waiting 3060s per scan is no longer acceptable—and now **must be achieved within a 333scan/day free quota**. |
### Gap in Existing Tools
* SaaSonly scanners cant run in regulated or disconnected environments.
* Monolithic opensource scanners are hardwired to Trivy or Syft formats, lacking delta optimisation.
* Few products expose **PolicyasCode** with full UI editing **and** history audit in a single package.
* None address quotaaware throttling without hidden paywalls.
**StellaOps** fills this gap by combining *speed*, *modular openness*, *sovereign readiness* **and transparent quota limits**—making thorough supplychain security attainable for every team, not just cloudnative startups.
---
*Last updated: 14 Jul 2025*

121
docs/02_WHY.md Normal file
View File

@ -0,0 +1,121 @@
# 2 · WHYWhyStellaOps Exists
> Explaining the concrete pain we solve, why the world needs **one more** DevSecOps
> platform, and the success signals that prove we are on the right track.
Softwaresupplychain attacks, licencerisk, and incomplete SBOM coverage slow
teams and compliance audits to a crawl. Most existing scanners:
* **Assume Internet** access for CVE feeds or SaaS backends.
* **Parse an entire image** every build (no layerdelta optimisation).
* **Accept a single SBOM format** (usually Trivy JSON) and choke on anything else.
* Offer **no builtin policy history / audit trail**.
* Require 3060s walltime per scan, an order of magnitude slower than modern CI
expectations.
* **Hide quota limits** or throttle without warning once you move past free trials.
---
# 1 FreeTier Quota  Why **333**?
The limit of **333 SBOM scans per UTC day** was not chosen at random.
| Constraint | Analysis | Outcome |
|------------|----------|---------|
| **SMB workload** | Internal survey across 37 SMBs shows median **210** container builds/day (p95 ≈290). | 333 gives 1.6× headroom without forcing a paid tier. |
| **Cost of feeds** | Hosting, Trivy DB mirrors & CVE merge traffic average **≈ $14/1000 scans**. | 333/day yields <$5 infra cost per user — sustainable for an OSS project. |
| **Incentive to upgrade** | Larger orgs (> 300 builds/day) gain ROI from Plus/Pro tiers anyway. | Clear upsell path without hurting hobbyists. |
> **In one sentence:**  *333 scans cover the daily needs of a typical small /
> medium business, keep free usage genuinely useful and still leave a financial
> runway for future development*.
## 1.1 How the Quota Is Enforced (1minute view)
* Backend loads the **Quota plugin** at startup.
* Every `/scan` call passes the callers **ClientJWT** to the plugin.
* The plugin **increments a counter in Redis** under
`quota:<token>:<yyyymmdd>` (expires at UTC midnight).
* Soft waitwall (5s) after limit; hard waitwall (60s) after 30 blocked calls.
* For **offline installs**, a *1month validity ClientJWT* ships inside every
**Offline Update Kit (OUK)** tarball. Uploading the OUK refreshes the token
automatically.
Detailed sequence living in **30_QUOTA_ENFORCEMENT_FLOW.md**.
---
## 2 · Why *Another* DevSecOps Product?  Macro Drivers
| Driver | Evidence | Implication for Tooling |
|--------|----------|-------------------------|
| **Exploding supplychain attacks** | Sonatype2024 report shows **742%** growth since2020. | SBOMs & provenance checks must be default, not “bestpractice”. |
| **Regulation tsunami** | • USEO14028 & NISTSP800218<br>• EUCyberResilienceAct (CRA) in force 2026<br>• Russian ФЗ187 for КИИ | Vendors must *attest* build provenance (SLSA) and store tamperproof SBOMs. |
| **Runtimecost intolerance** | Pipelines build hundreds of images/day; waiting >10s per scan breaks SLA. | Need **deltaaware** engines that reuse layer analyses (<1s warm scans). |
| **Airgap & sovereignty demands** | Finance/defence prohibit outbound traffic; data must stay onprem. | Ship **selfcontained registry + CVE DB** and run offline. |
| **Predictable freetier limits** | Teams want clarity, not surprise throttling. | Provide **transparent 333 scans/day quota**, early banner & graceful waitwall. |
> **Therefore:** The market demands a **modular, SBOMfirst, sub5s, 100% selfhosted**
> platform **with a transparent freetier quota**—precisely the niche StellaOps targets.
---
## 3 · Gap in Current Tooling
* Trivy / Syft create SBOMs but reanalyse **every** layer wasted minutes/day.
* Policy engines (OPA/Rego) are separate binaries, with no UI or change history.
* No mainstream OSS bundle ships an **anonymous internal registry** for airgapped pulls.
* Provenance attestation (SLSA) and Rekor transparency logs remain bringyourown”.
* Free tiers either stop at 100 scans **or** silently throttle; none announce a **clear 333/day allowance**.
---
## 4 · WhyStellaOps Can Win
1. **Speed First**  DeltaSBOM flow uses cached layers to hit `<1s` warm scans.
2. **MultiFormat Ready**  Autodetects TrivyJSON, SPDXJSON, CycloneDXJSON; UI
lets teams choose perproject defaults.
3. **Offline by Default**  Ships an **anonymous internal Docker registry**
(`StellaOps.Registry`) plus Redis, Mongo, CVE DB, and UI in a single compose up.
4. **Open & Modular**  .NET hotload plugins (`StellaOpsAttestor`, future scanners)
under AGPL; anyone can extend.
5. **Policy as Code**  YAML rules today, upgrade path to OPA/Rego with history stored
in Mongo via `StellaOps.MutePolicies`.
6. **SovereignReady**  Russianlanguage UI, local vulnerability mirrors, zero
telemetry by default.
7. **Honest Freetier Boundaries**  Clear **333 scans/day** limit, early banner at 200 and predictable waitwallno hidden throttling.
---
## 5 · Success Criteria Signals We Solve the Problem
* **Performance:** P95 scan <5s on first pass; `<1s` for warm delta scans.
* **Compatibility:** SBOMs in at least three formats consumed by 3 downstream tools.
* **Adoption:** 1000 reported installs & 2000 binary downloads by Q22026.
* **Compliance:** Positive audits referencing CRA / NIST / SLSA readiness.
* **Community:** 15 firsttime contributors merged per quarter by 2026.
* **Transparency:** 0 support tickets complaining about mystery throttling”.
---
## 6 · NonGoals (20252027)
* Multitenant SaaS offering.
* Automatic fixPR generation (left to ecosystem).
* Windows container **scanning** (Windows *agents* are on the 12month roadmap).
---
## 7 · Stakeholder PainPoint Recap
| Persona | Pain Today | StellaOps Solution |
|---------|------------|---------------------|
| **Dev** | My CI fails for 45s on every push.” | <5s initial, <1s warm scans. |
| **SecOps** | Separate tools for SBOM, policy, and audit. | Unified UI + YAML / Rego policies with history. |
| **Infra** | Internetblocked site; no public pulls allowed. | Offline compose bundle + internal registry. |
| **Compliance** | Need CRAready provenance by 2026. | Future `StellaOpsAttestor` SLSA + Rekor integration. |
| **Budget owner** | Fears hidden overage charges in free tiers. | Transparent 333 scans/day limit, visible in UI/API. |
---
*Last updated: 14Jul2025 (sync with freetier quota rev2.0).*

99
docs/03_VISION.md Normal file
View File

@ -0,0 +1,99 @@
# 3 · ProductVision — **StellaOps**
*(v1.3  12Jul2025 · supersedesv1.2; expanded with ecosystem integration, refined metrics, and alignment to emerging trends)*
---
## 0Preamble
This Vision builds on the purpose and gap analysis defined in **01WHY**.
It paints a threeyear “northstar” picture of success for the opensource project and sets the measurable guardrails that every roadmap item must serve, while fostering ecosystem growth and adaptability to trends like SBOM mandates, AIassisted security **and transparent usage quotas**.
---
## 1NorthStar Vision Statement (2027)
> *By mid2027, StellaOps is the fastest, mosttrusted selfhosted SBOM scanner. Developers expect vulnerability feedback in **five seconds or less**—even while the free tier enforces a transparent **333 scans/day** limit with graceful waiting. The project thrives on a vibrant plugin marketplace, weekly community releases, transparent governance, and seamless integrations with major CI/CD ecosystems—while never breaking the fivesecond promise.*
---
## 2Outcomes & Success Metrics
| KPI (communitycentric) | Baseline Jul2025 | Target Q22026 | NorthStar 2027 |
| -------------------------------- | ----------------- | -------------- | --------------- |
| ⭐Gitea /GitHub stars | 0 | 4000 | 10000 |
| Weekly active Docker pulls | 0 | 1500 | 4000 |
| P95 SBOM scan time (alpine) | 5s | **5s** | **4s** |
| Freetier scan satisfaction* | n/a | ≥90% | ≥95% |
| Firsttimecontributor PRs /qtr | 0 | 15 | 30 |
\*Measured via anonymous telemetry *optin only*: ratio of successful scans to `429 QuotaExceeded` errors.
---
## 3Strategic Pillars
1. **SpeedFirst** preserve the sub5s P95 walltime; any feature that hurts it must ship behind a toggle or plugin. **Quota throttling must apply a soft 5s delay first, so “speed first” remains true even at the limit.**
2. **OfflinebyDesign** every byte required to scan ships in public images; Internet access is optional.
3. **ModularForever** capabilities land as hotload plugins; the monolith can split without rewrites.
4. **CommunityOwnership** ADRs and governance decisions live in public; new maintainers elected by meritocracy.
5. **ZeroSurprise Upgrades & Limits** SemVer discipline; `main` is always installable; minor upgrades never break CI YAML **and freetier limits are clearly documented, with early UI warnings.**
6. **Ecosystem Harmony** Prioritise integrations with popular OSS tools (e.g., Trivy extensions, BuildKit hooks) to lower adoption barriers.
---
## 4Roadmap Themes (1824months)
| Horizon | Theme | Example EPIC |
| ------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Q32025** (3mo) | **Core Stability & UX** | Onecommand installer; darkmode UI; baseline SBOM scanning; **Freetier Quota Service (333 scans/day, early banner, waitwall).** |
| 612mo | *Extensibility* | Scanservice microsplit PoC; community plugin marketplace beta. |
| 1218mo | *Ecosystem* | Community plugin marketplace launch; integrations with Syft and Harbor. |
| 1824mo | *Resilience & Scale* | Redis Cluster autosharding; AIassisted triage plugin framework. |
*(Granular decomposition lives in 25_LEDGER.md.)
---
## 5Stakeholder Personas & Benefits
| Persona | Core Benefit |
| --------------------- | ---------------------------------------------------------------- |
| Solo OSS maintainer | Laptop scans in **5s**; zero cloud reliance. |
| CI Platform Engineer | Singlebinary backend + Redis; stable YAML integrations. |
| Security Auditor | AGPL code, traceable CVE sources, reproducible benchmarks. |
| Community Contributor | Plugin hooks and goodfirst issues; meritbased maintainer path. |
| Budgetconscious Lead | Clear **333 scans/day** allowance before upgrades are required. |
(See **01WHY §3** for detailed painpoints & evidence.)
---
## 6NonGoals (20252027)
* Multitenant SaaS offering.
* Automated “fix PR” generation.
* Proprietary compliance certifications (left to downstream distros).
* Windows **container** scanning (agents only).
---
## 7Review & Change Process
* **Cadence:** product owner leads a public Vision review every **2 sprints (≈1quarter)**.
* **Amendments:** material changes require PR labelled `type:vision` + two maintainer approvals.
* **Versioning:** bump patch for typo, minor for KPI tweak, major if NorthStar statement shifts.
* **Community Feedback:** Open GitHub Discussions for input; incorporate topvoted suggestions quarterly.
---
## 8·Change Log
| Version | Date | Note (highlevel) |
| ------- | ----------- | ----------------------------------------------------------------------------------------------------- |
| v1.4 | 14Jul2025 | First public revision reflecting quarterly roadmap & KPI baseline. |
| v1.3 | 12Jul2025 | Expanded ecosystem pillar, added metrics/integrations, refined non-goals, community persona/feedback. |
| v1.2 | 11Jul2025 | Restructured to link with WHY; merged principles into StrategicPillars; added review §7 |
| v1.1 | 11Jul2025 | Original OSSonly vision |
| v1.0 | 09Jul2025 | First public draft |
*(End of ProductVision v1.3)*

34
docs/04_FEATURE_MATRIX.md Normal file
View File

@ -0,0 +1,34 @@
#4 · FeatureMatrix — **StellaOps**
*(rev2.0 · 14Jul2025)*
| Category | Capability | Free Tier (≤333 scans/day) | Community Plugin | Commercial AddOn | Notes / ETA |
| ---------------------- | ------------------------------------- | ----------------------------- | ----------------- | ------------------- | ------------------------------------------ |
| **SBOM Ingestion** | TrivyJSON, SPDXJSON, CycloneDXJSON | ✅ | — | — | Autodetect on upload |
| | **DeltaSBOM Cache** | ✅ | — | — | Warm scans <1s |
| **Scanning** | CVE lookup via local DB | | | | Update job ships weekly feeds |
| | Licencerisk detection |  (roadmap Q42025) | | | SPDX licence list |
| **Policy Engine** | YAML rules | | | | InUI editor |
| | OPA / Rego |  (βQ12026) | plugin | | Plugin enables Rego |
| **Registry** | Anonymous internal registry | | | | `StellaOps.Registry` image |
| **Attestation** | Cosign signing |  (Q12026) | | | Requires `StellaOpsAttestor` |
| | SLSA provenance v1.0 | | |  (commercial 2026) | Enterprise need |
| | Rekor transparency log | | plugin | | Airgap replica support |
| **Quota & Throttling** | 333 scans/day soft limit | | | | Yellow banner at200, waitwall postlimit |
| | Usage API (`/quota`) | | | | CI can poll remaining scans |
| **User Interface** | Dark / light mode | | | | Autodetect OS theme |
| | Russian localisation | | | | Default if `AcceptLanguage: ru` |
| | Audit trail | | | | Mongo history |
| **Deployment** | Docker Compose bundle | | | | Singlenode |
| | Helm chart (K8s) | | | | Horizontal scaling |
| | Highavailability split services | | | (AddOn) | HA Redis & Mongo |
| **Extensibility** | .NET hotload plugins | | N/A | | AGPL reference SDK |
| | Community plugin marketplace | |  (βQ22026) | | Moderated listings |
| **Telemetry** | Optin anonymous metrics | | | | Required for quota satisfaction KPI |
| **Quota & Tokens** | **ClientJWT issuance** | (online 12h token) | | | `/connect/token` |
| | **Offline ClientJWT (30d)** | via OUK | | | Refreshed monthly in OUK |
> **Legend:** ✅ =Included=Planned=Not applicable
> Rows marked “Commercial AddOn” are optional paid components shipping outside the AGPLcore; everything else is FOSS.
---
*Last updated: 14 Jul 2025 (quota rev2.0).*

View File

@ -0,0 +1,204 @@
# SYSTEMREQUIREMENTSSPECIFICATION
StellaOps · selfhosted supplychainsecurity platform
> **Audience** core maintainers and external contributors who need an
> authoritative checklist of *what* the software must do (functional
> requirements) and *how well* it must do it (nonfunctional
> requirements). Implementation details belong in Module Specs
> or ADRs—**not here**.
---
## 1·Purpose & Scope
This SRS defines everything the **v0.8beta** release of _StellaOps_ must do, **including the new Freetier quota of 333 SBOM scans per token per UTC day**.
Scope includes core platform, CLI, UI, quota layer, and plugin host; commercial or closedsource extensions are explicitly outofscope.
---
## 2·References
* [02_WHY.md](02_WHY.md)  market gap & problem statement
* [03_VISION.md](03_VISION.md)  northstar, KPIs, quarterly themes
* [07_HIGH_LEVEL_ARCHITECTURE.md](07_HIGH_LEVEL_ARCHITECTURE.md)  context & data flow diagrams
* [08_MODULE_SPECIFICATIONS.md](08_MODULE_SPECIFICATIONS.md)  component APIs & plugin contracts
* [09_API_CLI_REFERENCE.md](09_API_CLI_REFERENCE.md)  REST & CLI surface
---
## 3·Definitions & Acronyms
| Term | Meaning |
|------|---------|
| **SBOM** | Software Bill of Materials |
| **Delta SBOM** | Partial SBOM covering only image layers not previously analysed |
| **Registry** | Anonymous, readonly Docker Registry v2 hosted internally |
| **OPA** | Open Policy Agent (Rego policy engine) |
| **Muting Policy** | Rule that downgrades or ignores specific findings |
| **SLSA** | Supplychain Levels for Software Artifacts (provenance framework) |
| **Rekor** | Sigstore transparency log for signatures |
---
## 4·Overall System Description
The platform consists of:
* **StellaOps Backend** REST API, queue, policy engine, DB.
* **StellaOps.Registry** internal container registry for agents.
* **SanTech Agent** extracts SBOMs; supports multiformat & delta.
* **Zastava Agent** enforcement hook for admissioncontrol scenarios.
* **WebUI** React/Next.js SPA consuming backend APIs.
* **Plugins** hotload binaries extending scanners, attestations, etc.
All services run in Docker Compose or Kubernetes with optional Internet
access.
---
## 5·Functional Requirements (FR)
### 5.1 Core Scanning
| ID | Requirement | Priority | Verification |
|----|-------------|----------|--------------|
| F1 | System SHALL ingest **TrivyJSON, SPDXJSON, CycloneDXJSON** files. | MUST | UTSBOM001 |
| F2 | System SHALL **autodetect** SBOM type when `sbomType` param omitted. | MUST | UTSBOM002 |
| F3 | System SHALL **cache analysed layers** and reuse them in subsequent scans. | MUST | ITCACHE001 |
| F4 | System SHALL **enforce a soft limit of 333 scans per token per UTC day**. | MUST | ITQUOTA001 |
| F4a | Remaining quota SHALL be **persisted in Redis** under key `quota:<token>:<yyyymmdd>`. | MUST | UTQUOTAREDIS |
| F4b | Exhausted quota SHALL trigger **HTTP429** with `RetryAfter` header set to midnight UTC. | MUST | ITQUOTA002 |
| F4c | When quota is ≤ 40% remaining, **UI banner** MUST turn yellow and show countdown. | SHOULD | UIE2E005 |
| F4d | `/quota` endpoint SHALL return JSON `{"limit":333,"remaining":N,"resetsAt":"<ISO8601>"}`. | SHOULD | APIDOC003 |
| F5 | Policy engine SHALL evaluate **YAML rules** against scan results. | MUST | UTPOL001 |
| F6 | Hotpluggable .NET plugins SHALL be loadable **without service restart**. | MUST | ITPLUGIN001 |
| F7 | CLI (`stella scan`) SHOULD exit **nonzero** when CVSS≥7 vulnerabilities found. | SHOULD | CLINT003 |
| *(… all previously documented F8  F12 rows retained unchanged …)* |
### 5.2 Internal Docker Repository
| Ref | Requirement |
|-----|-------------|
| **FRREPO1** | Platform SHALL include **StellaOps.Registry** exposing Docker Registry v2 API (ports 5000/443). |
| **FRREPO2** | Registry SHALL allow anonymous, *readonly* pulls for at least three images:<br>`stella/sbombuilder`<br>`stella/santech`<br>`stella/zastava`. |
| **FRREPO3** | Registry MAY enable optional basicauth without code changes. |
### 5.3 SBOM Generation & Handling
| Ref | Requirement |
|-----|-------------|
| **FRSBOM1** | SBOM builder SHALL produce TrivyJSON **and** at least one additional format: SPDXJSON and CycloneDXJSON. |
| **FRSBOM2** | For every generated SBOM, builder SHALL create a sidecar file `<image>.sbom.type` containing the format identifier. |
| **FRSBOM3** | SanTech Agent SHALL read the `.sbom.type` file and include `sbomType` parameter when uploading. |
| **FRSBOM4** | Backend SHALL autodetect SBOM type when parameter is missing. |
| **FRSBOM5** | UI Settings SHALL expose a dropdown to select default SBOM format (systemwide fallback). |
#### 5.3.1 Delta SBOM (layer reuse)
| Ref | Requirement |
|-----|-------------|
| **FRDELTA1** | Builder SHALL compute SHA256 digests of each image layer and POST array to `/layers/missing`; response time ≤20ms (P95). |
| **FRDELTA2** | Builder SHALL generate SBOM **only** for layers returned as “missing”. |
| **FRDELTA3** | Endtoend warm scan time (image differing by ≤2 layers) SHALL be ≤1s (P95). |
### 5.4 Policy as Code (Muting & Expiration)
| Ref | Requirement |
|-----|-------------|
| **FRPOLICY1** | Backend SHALL store policies as YAML by default, convertible to Rego for advanced usecases. |
| **FRPOLICY2** | Each policy change SHALL create an immutable history record (timestamp, actor, diff). |
| **FRPOLICY3** | REST endpoints `/policy/import`, `/policy/export`, `/policy/validate` SHALL accept YAML or Rego payloads. |
| **FRPOLICY4** | WebUI Policies tab SHALL provide Monaco editor with linting for YAML and Rego. |
| **FRPOLICY5** | **StellaOps.MutePolicies** module SHALL expose CLI `stella policies apply --file scanpolicy.yaml`. |
### 5.5 SLSA Attestations & Rekor (TODO >6mo)
| Ref | Requirement |
|-----|-------------|
| **FRSLSA1** | **TODO** Generate provenance in SLSAProvenance v0.2 for each SBOM. |
| **FRREKOR1** | **TODO** Sign SBOM hashes and upload to local Rekor mirror; verify during scan. |
### 5.6 CLI & API Interface
| Ref | Requirement |
|-----|-------------|
| **FRCLI1** | CLI `stella scan` SHALL accept `--sbom-type {trivy,spdx,cyclonedx,auto}`. |
| **FRAPI1** | API `/scan` SHALL accept `sbomType` query/body field (optional). |
| **FRAPI2** | API `/layers/missing` SHALL accept JSON array of digests and return JSON array of missing digests. |
---
## 6·NonFunctional Requirements (NFR)
| Ref | Category | Requirement |
|-----|----------|-------------|
| **NFRPERF1** | Performance | P95 cold scan ≤5s; warm ≤1s (see **FRDELTA3**). |
| **NFRPERF2** | Throughput | System shall sustain 60 concurrent scans on 8core node without queue depth >10. |
| **NFRAVAIL1** | Availability | All services shall start offline; any Internet call must be optional. |
| **NFRSCAL1** | Scalability | Horizontal scaling via Kubernetes replicas for backend, Redis Sentinel, Mongo replica set. |
| **NFRSEC1** | Security | All interservice traffic shall use TLS or localhost sockets. |
| **NFRCOMP1** | Compatibility | Platform shall run on x8664 Linux kernel ≥5.10; Windows agents (TODO>6mo) must support Server 2019+. |
| **NFRI18N1** | Internationalisation | UI must support RU and EN locale toggles. |
| **NFROBS1** | Observability | Export Prometheus metrics for scan duration, queue length, policy eval duration. |
---
##7Acceptance Criteria <a id="7-acceptance-criteria"></a>
1. Issue 333 `/scan` calls; 334th returns **HTTP429** and `RetryAfter`.
2. Redis failure during test → API returns **0 remaining** & warns in logs.
3. UI banner activates at 133 remaining; clears next UTC midnight.
---
## 8·System Interfaces
### 8.1 External APIs
*(This is the complete original table, plus new `/quota` row.)*
| Path | Method | Auth | Quota | Description |
|------|--------|------|-------|-------------|
| `/scan` | POST | Bearer | ✅ | Submit SBOM or `imageRef` for scanning. |
| `/quota` | GET | Bearer | ❌ | Return remaining quota for current token. |
| `/policy/rules` | GET/PUT | Bearer+RBAC | ❌ | CRUD YAML or Rego policies. |
| `/plugins` | POST/GET | Bearer+Admin | ❌ | Upload or list plugins. |
```bash
GET /quota
Authorization: Bearer <token>
200 OK
{
"limit": 333,
"remaining": 121,
"resetsAt": "2025-07-14T23:59:59Z"
}
```
## 9 ·Assumptions & Constraints
* Hardware reference: 8vCPU, 8GB RAM, NVMe SSD.
* MongoDB and Redis run colocated unless horizontal scaling enabled.
* All docker images tagged `latest` are immutable (CI process locks digests).
* Rego evaluation runs in embedded OPA Golibrary (no external binary).
---
## 10·Future Work (Beyond 12Months)
* Rekor transparency log crosscluster replication.
* AIassisted falsepositive triage plugin.
* Clusterwide injection for live runtime scanning.
---
## 11·Revision History
| Version | Date | Notes |
|---------|------|-------|
| **v1.2** | 11Jul2025 | Commercial references removed; plugin contract (§3.3) and new NFR categories added; added User Classes & Traceability. |
| v1.1 | 11Jul2025 | Split out RUspecific items; OSS scope |
| v1.0 | 09Jul2025 | Original unified SRS |
*(End of SystemRequirements Specification v1.2core)*

View File

@ -0,0 +1,413 @@
#7 · HighLevel Architecture — **StellaOps**
---
##0Purpose &Scope
Give contributors, DevOps engineers and auditors a **complete yet readable map** of the Core:
* Major runtime components and message paths.
* Where plugins, CLI helpers and runtime agents attach.
* Technology choices that enable the sub5second SBOM goal.
* Typical operational scenarios (pipeline scan, mute, nightly rescan, etc.).
Anything enterpriseonly (signed PDF, Cryptospecific TLS, LDAP, enforcement) **must arrive as a plugin**; the Core never hardcodes those concerns.
---
##1Component Overview
| # | Component | Responsibility |
|---|-----------|---------------|
| 1 | **API Gateway** | REST endpoints (`/scan`, `/quota`, **`/token/offline`**); token auth; quota enforcement |
| 2 | **Scan Service** | SBOM parsing, DeltaSBOM cache, vulnerability lookup |
| 3 | **Policy Engine** | YAML / (optional) Rego rule evaluation; verdict assembly |
| 4 | **Quota Service** | Pertoken counters; **333 scans/day**; waits & HTTP 429 |
| 5 | **ClientJWT Issuer** | Issues 30day offline tokens; bundles them into OUK |
| 5 | **Registry** | Anonymous internal Docker registry for agents, SBOM uploads |
| 6 | **Web UI** | React/Blazor SPA; dashboards, policy editor, quota banner |
| 7 | **Data Stores** | **Redis** (cache, quota) & **MongoDB** (SBOMs, findings, audit) |
| 8 | **Plugin Host** | Hotload .NET DLLs; isolates community plugins |
| 9 | **Agents** | `sbombuilder`, `SanTech` scanner CLI, future `StellaOpsAttestor` |
```mermaid
flowchart TD
subgraph "External Actors"
DEV["Developer / DevSecOps / Manager"]
CI["CI/CD Pipeline (e.g., SanTech CLI)"]
K8S["Kubernetes Cluster (e.g., Zastava Agent)"]
end
subgraph "Stella Ops Runtime"
subgraph "Core Services"
CORE["Stella Core<br>(REST + gRPC APIs, Orchestration)"]
REDIS[("Redis<br>(Cache, Queues, Trivy DB Mirror)")]
MONGO[("MongoDB<br>(Optional: Long-term Storage)")]
POL["Mute Policies<br>(OPA & YAML Evaluator)"]
REG["StellaOps Registry<br>(Docker Registry v2)"]
ATT["StellaOps Attestor<br>(SLSA + Rekor)"]
end
subgraph "Agents & Builders"
SB["SBOM Builder<br>(Go Binary: Extracts Layers, Generates SBOMs)"]
SA["SanTech Agent<br>(Pipeline Helper: Invokes Builder, Triggers Scans)"]
ZA["Zastava Agent<br>(K8s Webhook: Enforces Policies, Inventories Containers)"]
end
subgraph "Scanners & UI"
TRIVY["Trivy Scanner<br>(Plugin Container: Vulnerability Scanning)"]
UI["Web UI<br>(Vue3 + Tailwind: Dashboards, Policy Editor)"]
CLI["Stella CLI<br>(CLI Helper: Triggers Scans, Mutes)"]
end
end
DEV -->|Browses Findings, Mutes CVEs| UI
DEV -->|Triggers Scans| CLI
CI -->|Generates SBOM, Calls /scan| SA
K8S -->|Inventories Containers, Enforces Gates| ZA
UI -- "REST" --> CORE
CLI -- "REST/gRPC" --> CORE
SA -->|Scan Requests| CORE
SB -->|Uploads SBOMs| CORE
ZA -->|Policy Gates| CORE
CORE -- "Queues, Caches" --> REDIS
CORE -- "Persists Data" --> MONGO
CORE -->|Evaluates Policies| POL
CORE -->|Attests Provenance| ATT
CORE -->|Scans Vulnerabilities| TRIVY
SB -- "Pulls Images" --> REG
SA -- "Pulls Images" --> REG
ZA -- "Pulls Images" --> REG
style DEV fill:#f9f,stroke:#333
style CI fill:#f9f,stroke:#333
style K8S fill:#f9f,stroke:#333
style CORE fill:#ddf,stroke:#333
style REDIS fill:#fdd,stroke:#333
style MONGO fill:#fdd,stroke:#333
style POL fill:#dfd,stroke:#333
style REG fill:#dfd,stroke:#333
style ATT fill:#dfd,stroke:#333
style SB fill:#fdf,stroke:#333
style SA fill:#fdf,stroke:#333
style ZA fill:#fdf,stroke:#333
style TRIVY fill:#ffd,stroke:#333
style UI fill:#ffd,stroke:#333
style CLI fill:#ffd,stroke:#333
```
* **Developer / DevSecOps / Manager** browses findings, mutes CVEs, triggers scans.
* **Santech CLI** generates SBOMs and calls `/scan` during CI.
* **Zastava Agent** inventories live containers; Core ships it in *passive* mode only (no kill).
###1.1ClientJWT Lifecycle (offline aware)
1. **Online instance** user signs in → `/connect/token` issues JWT valid 12h.
2. **Offline instance** JWT with `exp 30days` ships in OUK; backend
**resigns** and stores it during import.
3. Tokens embed a `tier` claim (“Free”) and `maxScansPerDay: 333`.
4. On expiry the UI surfaces a red toast **7days** in advance.
---
##2·Component Responsibilities (runtime view)
| Component | Core Responsibility | Implementation Highlights |
| -------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| **Stella Core** | Orchestrates scans, persists SBOM blobs, serves REST/gRPC APIs, fans out jobs to scanners & policy engine. | .NET8, CQRS, Redis Streams; pluggable runner interfaces. |
| **SBOM Builder** | Extracts image layers, queries Core for *missing* layers, generates SBOMs (multiformat), uploads blobs. | Go binary; wraps Trivy & Syft libs. |
| **SanTech Agent** | Pipelineside helper; invokes Builder, triggers scan, streams progress back to CI/CD. | Static musl build. |
| **Zastava Agent** | K8s admission webhook enforcing policy verdicts before Pod creation. | Rust for sub10ms latencies. |
| **UI** | Angular17 SPA for dashboards, settings, policy editor. | Tailwind CSS; Webpack module federation (future). |
| **Redis** | Cache, queue, TrivyDB mirror, layer diffing. | Single instance or Sentinel. |
| **MongoDB** (opt.) | Longterm SBOM & policy audit storage (>180days). | Optional; enabled via flag. |
| **StellaOps.Registry** | Anonymous readonly Docker v2 registry with optional Cosign verification. | `registry :2` behind nginx reverse proxy. |
| **StellaOps.MutePolicies** | YAML/Rego evaluator, policy version store, `/policy/*` API. | Embeds OPAWASM; falls back to `opa exec`. |
| **StellaOpsAttestor** | Generate SLSA provenance & Rekor signatures; verify on demand. | Sidecar container; DSSE + Rekor CLI. |
All crosscomponent calls use dependencyinjected interfaces—no
intracomponent reachins.
---
##3·Principal Backend Modules & Plugin Hooks
| Namespace | Responsibility | Builtin Tech / Default | Plugin Contract |
| --------------- | -------------------------------------------------- | ----------------------- | ------------------------------------------------- |
| `configuration` | Parse env/JSON, healthcheck endpoint | .NET9 Options | `IConfigValidator` |
| `identity` | Embedded OAuth2/OIDC (OpenIddict 6) | MIT OpenIddict | `IIdentityProvider` for LDAP/SAML/JWT gateway |
| `pluginloader` | Discover DLLs, SemVer gate, optional Cosign verify | Reflection + Cosign | `IPluginLifecycleHook` for telemetry |
| `scanning` | SBOM & imageflow orchestration; runner pool | Trivy CLI (default) | `IScannerRunner` e.g., Grype, Copacetic, Clair |
| `feedmerger` | Nightly NVD merge & feed enrichment | Hangfire job | dropin `*.Schedule.dll` for OSV, GHSA, BDU feeds |
| `tls` | TLS provider abstraction | OpenSSL | `ITlsProvider` for GOST, SMseries, custom suites |
| `reporting` | Render HTML/PDF reports | RazorLight | `IReportRenderer` |
| `ui` | Angular SPA & i18n | Angular 17 | new locales via `/locales/{lang}.json` |
| `scheduling` | Cron + retries | Hangfire | any recurrent job via `*.Schedule.dll` |
```mermaid
classDiagram
class configuration
class identity
class pluginloader
class scanning
class feedmerger
class tls
class reporting
class ui
class scheduling
class AllModules
configuration ..> identity : Uses
identity ..> pluginloader : Authenticates Plugins
pluginloader ..> scanning : Loads Scanner Runners
scanning ..> feedmerger : Triggers Feed Merges
tls ..> AllModules : Provides TLS Abstraction
reporting ..> ui : Renders Reports for UI
scheduling ..> feedmerger : Schedules Nightly Jobs
note for scanning "Pluggable: ISScannerRunner<br>e.g., Trivy, Grype"
note for feedmerger "Pluggable: *.Schedule.dll<br>e.g., OSV, GHSA Feeds"
note for identity "Pluggable: IIdentityProvider<br>e.g., LDAP, SAML"
note for reporting "Pluggable: IReportRenderer<br>e.g., Custom PDF"
```
**When remaining =0:**
API returns `429 Too Many Requests`, `RetryAfter: <UTCmidnight>` (sequence omitted for brevity).
---
##4·Data Flows
###4.1SBOMFirst (≤5s P95)
Builder produces SBOM locally, so Core never touches the Docker
socket.
Trivy path hits ≤5s on alpine:3.19 with warmed DB.
Imageunpack fallback stays ≤10s for 200MB images.
```mermaid
sequenceDiagram
participant CI as CI/CD Pipeline (SanTech Agent)
participant SB as SBOM Builder
participant CORE as Stella Core
participant REDIS as Redis Queue
participant RUN as Scanner Runner (e.g., Trivy)
participant POL as Policy Evaluator
CI->>SB: Invoke SBOM Generation
SB->>CORE: Check Missing Layers (/layers/missing)
CORE->>REDIS: Query Layer Diff (SDIFF)
REDIS-->>CORE: Missing Layers List
CORE-->>SB: Return Missing Layers
SB->>SB: Generate Delta SBOM
SB->>CORE: Upload SBOM Blob (POST /scan(sbom))
CORE->>REDIS: Enqueue Scan Job
REDIS->>RUN: Fan Out to Runner
RUN->>RUN: Perform Vulnerability Scan
RUN-->>CORE: Return Scan Results
CORE->>POL: Evaluate Mute Policies
POL-->>CORE: Policy Verdict
CORE-->>CI: JSON Verdict & Progress Stream
Note over CORE,CI: Achieves ≤5s P95 with Warmed DB
```
###4.2Delta SBOM
Builder collects layer digests.
`POST /layers/missing` → Redis SDIFF → missing layer list (<20ms).
SBOM generated only for those layers and uploaded.
###4.3Feed Enrichment
```mermaid
sequenceDiagram
participant CRON as Nightly Cron (Hangfire)
participant FM as Feed Merger
participant NVD as NVD Feed
participant OSV as OSV Plugin (Optional)
participant GHSA as GHSA Plugin (Optional)
participant BDU as BDU Plugin (Optional)
participant REDIS as Redis (Merged Feed Storage)
participant UI as Web UI
CRON->>FM: Trigger at 00:59
FM->>NVD: Fetch & Merge NVD Data
alt Optional Plugins
FM->>OSV: Merge OSV Feed
FM->>GHSA: Merge GHSA Feed
FM->>BDU: Merge BDU Feed
end
FM->>REDIS: Persist Merged Feed
REDIS-->>UI: Update Feed Freshness
UI->>UI: Display Green 'Feed Age' Tile
```
###4.4Identity & Auth Flow
OpenIddict issues JWTs via clientcredentials or password grant.
An IIdentityProvider plugin can delegate to LDAP, SAML or external OIDC
without Core changes.
---
##5·Runtime Helpers
| Helper | Form | Purpose | Extensible Bits |
|-----------|---------------------------------------|--------------------------------------------------------------------|-------------------------------------------|
| **SanTech** | Distroless CLI | Generates SBOM, calls `/scan`, honours threshold flag | `--engine`, `--pdf-out` piped to plugins |
| **Zastava** | Static Go binary / DaemonSet | Watches Docker/CRIO events; uploads SBOMs; can enforce gate | Policy plugin could alter thresholds |
---
##6·Persistence & Cache Strategy
| Store | Primary Use | Why chosen |
|----------------|-----------------------------------------------|--------------------------------|
| **Redis7** | Queue, SBOM cache, Trivy DB mirror | Sub1ms P99 latency |
| **MongoDB** | History>180d, audit logs, policy versions | Optional; documentoriented |
| **Local tmpfs**| Trivy layer cache (`/var/cache/trivy`) | Keeps disk I/O off hot path |
```mermaid
flowchart LR
subgraph "Persistence Layers"
REDIS[(Redis: Fast Cache/Queues<br>Sub-1ms P99)]
MONGO[(MongoDB: Optional Audit/History<br>>180 Days)]
TMPFS[(Local tmpfs: Trivy Layer Cache<br>Low I/O Overhead)]
end
CORE["Stella Core"] -- Queues & SBOM Cache --> REDIS
CORE -- Long-term Storage --> MONGO
TRIVY["Trivy Scanner"] -- Layer Unpack Cache --> TMPFS
style REDIS fill:#fdd,stroke:#333
style MONGO fill:#dfd,stroke:#333
style TMPFS fill:#ffd,stroke:#333
```
---
##7·Typical Scenarios
| # | Flow | Steps |
|---------|----------------------------|-------------------------------------------------------------------------------------------------|
| **S1** | Pipeline Scan & Alert | SanTech → SBOM → `/scan` → policy verdict → CI exit code & link to *Scan Detail* |
| **S2** | Mute Noisy CVE | Dev toggles **Mute** in UI → rule stored in Redis → next build passes |
| **S3** | Nightly Rescan | `SbomNightly.Schedule` requeues SBOMs (maskfilter) → dashboard highlights new Criticals |
| **S4** | Feed Update Cycle | `FeedMerger` merges feeds → UI *Feed Age* tile turns green |
| **S5** | Custom Report Generation | Plugin registers `IReportRenderer``/report/custom/{digest}` → CI downloads artifact |
```mermaid
sequenceDiagram
participant DEV as Developer
participant UI as Web UI
participant CORE as Stella Core
participant REDIS as Redis
participant RUN as Scanner Runner
DEV->>UI: Toggle Mute for CVE
UI->>CORE: Update Mute Rule (POST /policy/mute)
CORE->>REDIS: Store Mute Policy
Note over CORE,REDIS: YAML/Rego Evaluator Updates
alt Next Pipeline Build
CI->>CORE: Trigger Scan (POST /scan)
CORE->>RUN: Enqueue & Scan
RUN-->>CORE: Raw Findings
CORE->>REDIS: Apply Mute Policies
REDIS-->>CORE: Filtered Verdict (Passes)
CORE-->>CI: Success Exit Code
end
```
```mermaid
sequenceDiagram
participant CRON as SbomNightly.Schedule
participant CORE as Stella Core
participant REDIS as Redis Queue
participant RUN as Scanner Runner
participant UI as Dashboard
CRON->>CORE: Re-queue SBOMs (Mask-Filter)
CORE->>REDIS: Enqueue Filtered Jobs
REDIS->>RUN: Fan Out to Runners
RUN-->>CORE: New Scan Results
CORE->>UI: Highlight New Criticals
Note over CORE,UI: Focus on Changes Since Last Scan
```
---
##8·UIFastFacts
* **Stack** Angular17 + Vite dev server; Tailwind CSS.
* **State** Signals + RxJS for live scan progress.
* **i18n / l10n** JSON bundles served from `/locales/{lang}.json`.
* **ModuleStructure** Lazyloaded feature modules (`dashboard`, `scans`, `settings`); runtime route injection by UI plugins (roadmap Q22026).
---
##9·CrossCutting Concerns
* **Security** containers run nonroot, `CAP_DROP:ALL`, readonly FS, hardened seccomp profiles.
* **Observability** Serilog JSON, OpenTelemetry OTLP exporter, Prometheus `/metrics`.
* **Upgrade Policy** `/api/v1` endpoints & CLI flags stable across a minor; breaking changes bump major.
---
##10·Performance & Scalability
| Scenario | P95 target | Bottleneck | Mitigation |
|-----------------|-----------:|-----------------|-------------------------------------------------|
| SBOMfirst | ≤5s | Redis queue | More CPU, increase `ScannerPool.Workers` |
| Imageunpack | ≤10s | Layer unpack | Prefer SBOM path, warm Docker cache |
| High concurrency| 40rps | Runner CPU | Scale Core replicas + sidecar scanner services |
---
##11·Future Architectural Anchors
* **ScanService microsplit (gRPC)** isolate heavy runners for large clusters.
* **UI route plugins** dynamic Angular module loader (roadmap Q22026).
* **Redis Cluster** transparently sharded cache once sustained>100rps.
---
##12·Assumptions & Tradeoffs
Requires Docker/CRIO runtime; .NET9 available on hosts; Windows containers are outofscope this cycle.
Embedded auth simplifies deployment but may need plugins for enterprise IdPs.
Speed is prioritised over exhaustive feature parity with heavyweight commercial scanners.
---
##13·References & Further Reading
* **C4 Model** <https://c4model.com>
* **.NET Architecture Guides** <https://learn.microsoft.com/dotnet/architecture>
* **OSS Examples** Kubernetes Architecture docs, Prometheus design papers, Backstage.
---
##14·Change Log
*(Git history is authoritative; table for reader convenience.)*
| Date | Note |
|------------|--------------------------------------------------------------------------------------------------------|
| 20250713 | Added internal registry, multiformat SBOM, delta flow, Policy as Code, Attestor integration sections. |
| 20250712 | Reorganised doc around C4; added diagrams, tradeoffs, security notes. |
| 20250711 | Initial opensourcing pass  removed commercial references, added plugin hooks and UI details. |
---
##14Change Log
| Version | Date | Notes |
| ------- | ---------- | ------------------------------------------------------------------------------------------------ |
| v2.4 | 20250712 | New modules |
| v2.3 | 20250712 | Adopted C4 structure, added diagrams/trade-offs/security notes/contribution hooks/references. |
| v2.2 | 20250711 | Removed last commercial refs; added TLS/IdP/report plugin hooks; deeper UI & scenario sections; |
| v2.1 | 20250711 | Added scenarios, UI details, plugin depth. |
| v2.0 | 20250711 | Full rewrite. |
*(End of HighLevel Architecture v2.2)*

View File

@ -0,0 +1,372 @@
#8 · Detailed Module Specifications — **StellaOps**
_This document defines every backend/agent module that composes Stella Ops,
their public contracts, configuration keys and extension points._
---
##0Scope
Describes **every .NET, and Angular project** that ships in the OSS Core, the plugin contracts they expose, and the runtime artefacts (Dockerfiles, Compose files) used to build and operate them. Commercial capabilities appear *only* as extension points.
---
##1Repository Layout (flat)
~~~text
src/
│ docker-compose.yml
└─ docker-compose-library/
│ ├─ docker-compose.no-deps.yml
│ ├─ docker-compose.dep.redis.yml
│ ├─ docker-compose.dep.mongo.yml
│ ├─ docker-compose.dep.proxy.yml
│ ├─ docker-compose.dep.repository.yml
│ └─ docker-compose.local.yml
└─ backend/
│ ├─ Dockerfile
│ ├─ StellaOps.Web/
│ ├─ StellaOps.Common/
│ ├─ StellaOps.Plugins/
│ ├─ StellaOps.Configuration/
│ ├─ StellaOps.Localization/
│ ├─ StellaOps.TlsProvider.OpenSSL/
│ ├─ StellaOps.TlsProvider.OpenSSLGost/
│ ├─ StellaOps.TlsProvider.Plugin.CryptoPro/
│ ├─ StellaOps.VulnerabilityDatabase/
│ ├─ StellaOps.Scheduling/
│ ├─ StellaOps.Scheduling.SbomsRescan/
│ ├─ StellaOps.Scheduling.MutesExpire/
│ ├─ StellaOps.Scheduling.Plugin.CommonCveFeed/
│ ├─ StellaOps.Scheduling.Plugin.RussianCveFeed/
│ ├─ StellaOps.Scanners.Trivy/
│ ├─ StellaOps.Quota/
│ ├─ StellaOps.Reporting/
│ ├─ StellaOps.Notifications/
│ ├─ StellaOps.Notifications.Email/
│ ├─ StellaOps.Notifications.Plugin.MsTeams/
│ ├─ StellaOps.Authority/
│ ├─ StellaOps.Authority.AD/
│ ├─ StellaOps.Agent.Santech/
│ └─ StellaOps.Agent.Zastava/
└─ frontend/
├─ Dockerfile
├─ angular.json
├─ stella-ops-ui/
└─ libs/
├─ dashboard/
├─ scans/
├─ settings/
├─ core-ui/
└─ i18n/
~~~
All projects are referenced by **`StellaOps.sln`**; `dotnet publish -c Release -p:PublishSingleFile=true` builds a selfcontained **`StellaOps.Api`** binary (plugins load at runtime).
---
##2Shared Libraries
| Project | Purpose | Key Interfaces |
|---------|---------|----------------|
| `StellaOps.Common` | Serilog sinks, Redis key helpers, DTO primitives. | `RedisKeys`, `Result<T>` |
| `StellaOps.Plugins` | Plugin contracts + Cosign verification. | `IStellaPlugin`, `IScannerRunner`, `ITlsProvider`, `IScheduleJob` |
| `StellaOps.Localization` | Loads JSON locale bundles (backend & Angular). | `ILocaleProvider`, `CultureMiddleware` |
Angular JSONbundle workflow matches the official i18n guide.
---
##3Core Backend Projects
| Project | Responsibility | Extensibility |
|---------|----------------|---------------|
| **`StellaOps.Api`** | ASP.NET host; sourcegen autowires module endpoints. | Attributes `[MapRestController]`, `[MapHealth]`. |
| **`StellaOps.Configuration`** | Bind `appsettings.json` → typed options; `/health`. | `IConfigValidator`. |
| **`StellaOps.Quota`** | Enforces **Freetier quota** (333 scans/day) with earlywarning banner, 5s soft backoff, 60s waitwall. | Swappable via `IQuotaStore` (e.g., Postgres). |
| **`StellaOps.JwtIssuer` *(new)* | Issues, refreshes and validates **ClientJWTs**. For offline sites it produces a 30day token during OUK build and again on every OUK import. | `ITokenSigner` (e.g., HSM) |
| **`StellaOps.TlsProvider.OpenSSL`** | Default TLS suites. | New suites via `ITlsProvider` plugin. |
| **`StellaOps.TlsProvider.OpenSSLGost`** | GOST suites. | — |
| **`StellaOps.VulnerabilityDatabase`** | Feedmerge CLI writing Redis. | `IAdditionalFeedSource` (OSV, GHSA, BDU). |
| **`StellaOps.Scheduling`** | Hangfire host inside API . | Jobs via `IScheduleJob`. |
| **`StellaOps.Scheduling.SbomsRescan`** | Nightly SBOM rescan (`0 2 * * *`). | — |
| **`StellaOps.Scheduling.MutesExpire`** | Daily mute expiry cleanup. | — |
| **`StellaOps.Scanners.Trivy`** | Trivy CLI for SBOM & image scans. | Other engines implement `IScannerRunner`. |
| **`StellaOps.Reporting`** | RazorLight HTML reports. | `IReportRenderer` for SARIF, CycloneDX. |
| **`StellaOps.Notifications`** | DI contracts for alerts. | `INotifier`. |
| **`StellaOps.Notifications.Email`** | SMTP channel. | — |
| **`StellaOps.Authority`** | OAuth2 / OIDC via OpenIddict4 . | External IdPs via plugin. |
| **`StellaOps.Registry`** | readonly Docker registry for agents + SBOMbuilder | Registryv2 (nginxhardened) | `IRegistryProvider` |
| **`StellaOps.MutePolicies`** | store YAML / Rego policies, validate & version | MongoDB + Redis | `IPolicyStore` |
| **`StellaOps.Attestor`** *(TODO)*| SLSA provenance + Rekor verification | Sigstore Rekor | `IAttestor` |
##3·ModuleDetails
> _Only contracts and configuration that may change in the next two quarters are shown; for stable, unchanging keys see the inline XMLdoc in the codebase._
###3.1. StellaOps.Configuration
* **Responsibility** parse environment variables or `appsettings.json`; expose `/health`, `/metrics`.
* **Key extension point** `IConfigValidator` → validate & normalise custom settings before DI builds.
###3.2. StellaOps.Authority
* **Responsibility** ship with OpenIddict 6, supporting *clientcredentials* and *password* grants.
* `IIdentityProvider` plugin can delegate token issuance to LDAP, SAML, Keycloak 
###3.3. StellaOps.Scanners
* **Primary flow** SBOMfirst; falls back to imageunpack if SBOM absent.
* **MultiFormat Support** sidecar `.sbom.type` file; autodetects (`SPDXID:` or `bomFormat` heuristics).
* **Delta Layer Workflow** `POST /layers/missing` (`SET DIFF` on Redis) responds <20ms; SanTech passes only new layers.
* **Plugin contract evolution**
```csharp
// current
Task<ScanResult> RunAsync(Stream sbomJson, CancellationToken ct);
// v2 (preferred)
Task<ScanResult> RunAsync(Stream sbom, SbomFormat fmt, CancellationToken ct);
```
###3.5StellOps.Registry
* **Purpose** internal, anonymous **readonly** Docker registry to avoid GHCR / Docker Hub pulls.
* **Deployment** container `stellops.registry:2`; mounted volume `/var/lib/registry`; optional TLS via env vars.
| Key | Default | Notes |
|----------------------------------|---------|---------------------------------|
| `REGISTRY_READONLY` | `true` | Forces 403 on PUT, 405 on DELETE |
| `REGISTRY_STORAGE_DELETE_ENABLED`| `false` | Immutable tags |
**Plugin contract**  `IRegistryProvider.PullAsync(string imageRef)` for mapping to Artifactory, Harbor, etc.
---
###3.6StellaOps.MutePolicies
* **Purpose** central PolicyasCode store (YAML v1 now, Rego soon).
* **Persistence** current live rules in Redis (`policies:active`); immutable commits in Mongo `policies_history`.
| REST verb | Path | Description |
|-----------|---------------------|---------------------------|
| `GET` | `/policy/export` | download active YAML |
| `POST` | `/policy/import` | upload YAML / Rego file |
| `POST` | `/policy/validate` | lint without persisting |
**CLI** SanTech gains `--policy-file scan-policy.yaml`.
**Plugin contract** `IPolicyStore` for GitOps backends, Vault, etc.
---
###3.7. StellaOps.Attestor *(Planned  Q12026)*
Handles SLSA provenance docs and Rekor log verification.
```csharp
public interface IAttestor {
Task<ProvenanceDoc> CreateAsync(ImageRef img, Sbom sbom);
Task<bool> VerifyAsync(ProvenanceDoc doc);
}
```
###3.7. StellaOps.FeedMerger
Nightly Hangfire job (01:00) merges NVD JSON; plugins can provide ISourceFeed for OSV, GHSA, BDU feeds.
###3.8. StellOps.Tls
Abstracts TLS stack; default OpenSSL; ITlsProvider lets enterprises swap in GOST or SM cipher suites.
###3.9. StellaOps.Reporting
HTML / PDF generation via RazorLight; custom renderers via IReportRenderer.
###3.10 UI
Angular 17 SPA; lazyloaded feature modules, standalone component routes for UI plugins.
Static Go daemon / k8s DaemonSet; watches Docker/CRIO events; uploads SBOMs; optional enforce mode via policy plugin.
###3.11 StellaOps.Quota — **FreeTier Daily Quota Service**
**Responsibility**
* Track pertoken scan count (`quota:<token>` key in Redis).
* Reset counters at **00:00UTC** with key TTL.
* Inject HTTP headers
* `XStellaQuotaRemaining`
* `XStellaReset`
* Apply adaptive throttling:
* scans0199  normal;
* scans200332  UI banner flag `XStellaQuotaWarn:true`;
* scans333  respond **`429`** with escalating `RetryAfter` values
(5s for first 30 hits, then 60s).
* **Offline token awareness** if `token.valid == false` and
`OfflineMode == true`, return HTTP*451 ComplianceBlock* so that CLI gives a
clear actionable error.
* New config:
```json
"Quota": {
"OfflineGraceDays": 7 // show banner this many days before token expiry
}
```
**Interface**
```csharp
public interface IQuotaService
{
/// <summary>Returns true when the call is allowed.</summary>
Task<QuotaVerdict> CheckAsync(string token, CancellationToken ct);
}
public readonly record struct QuotaVerdict(
bool IsAllowed,
int Remaining,
DateTimeOffset ResetUtc,
TimeSpan RetryAfter);
```
**Configuration** (`appsettings.json` keys)
```json
"Quota": {
"FreeTierDailyLimit": 333,
"WarnThreshold": 200,
"SoftRetrySeconds": 5,
"HardRetrySeconds": 60
}
```
**Extensibility**
* Override storage by providing an `IQuotaStore` plugin for Postgres or Mongo.
* UI plugins can subscribe to SSE `/quota/events` for custom dashboards.
###3.12 StellaOps.JwtIssuer — new section
|API |Path| Notes|
|-----|----|-------|
|`POST /token/offline` | Adminonly. | Generates a 30d ClientJWT for airgapped clusters; returns ZIP that the admin can copy to the target host.
*OUK hook*
* OUK builder calls JwtIssuer.SignOfflineToken(exp=+30d).
* Drops client.jwt into ouk/root/.
* Backend OUK importer places file under /var/lib/stella/tokens/.
---
##4·Compose / Helm Snippet (reference)
```yaml
services:
registry:
image: stellops.registry:2
restart: unless-stopped
environment:
REGISTRY_READONLY: "true"
volumes:
- ./_registry:/var/lib/registry
ports:
- "5000:5000"
backend:
image: registry.local/stellops/backend:${TAG}
depends_on: [registry, redis]
```
---
##4Plugins (signrequired)
| Plugin | Contract | Notes |
|---------|----------|-------|
| `StellaOps.Notifications.Plugin.MsTeams` | `INotifier` | Sends cards to Teams webhooks. |
| `StellaOps.Authority.AD` | `IIdentityProvider` | LDAP/ActiveDirectory token issue. |
| `StellaOps.Scheduling.Plugin.CommonCveFeed` | `IScheduleJob` | Merges OSV & NVDJSON hourly. |
| `StellaOps.Scheduling.Plugin.RussianCveFeed` | `IScheduleJob` | Imports BDU XML daily. |
| `StellaOps.TlsProvider.Plugin.CryptoPro` | `ITlsProvider` | Binds CryptoPro shared libs. |
Cosign signatures are mandatory; loader rejects unsigned DLLs when `DisableUnsigned=false`.
---
##5Agents
###5.1`StellaOps.Agent.Santech`
Distroless CLI;
Returns exitcode1 on policy violation, enabling CI blocking.
* **Role** CI helper: Build SBOM, call `/scan`, exit nonzero on high severity.
* **Flags** `--engine`, `--threshold`, `--registry-pull-token`, `--pdf-out`, `--delta`, `--sbom-type`, `--policy-file.`.
* **Auth** OAuth2 *scanner* scope.
###5.2`StellaOps.Agent.Zastava`
* **Role** Passive container inventory uploads SBOMs via `/agent/sbom`.
* **Modes** `off`, `inventory` (Core default).
* No kernel driver (unlike Falco) .
---
##6Angular Frontend
| Package | Path | Feature | Lazy |
|---------|------|---------|------|
| **App** | `frontend/stella-ops-ui/` | Shell, auth guards. | |
| `dashboard` | `libs/dashboard/` | Live metrics tiles. | |
| `scans` | `libs/scans/` | List, detail, mute, diff. | |
| `settings` | `libs/settings/` | Feed cron, workers, TLS switch. | |
| `core-ui` | `libs/core-ui/` | Tailwind components. | |
| `i18n` | `libs/i18n/` | Runtime locale switch, pipe. | |
Lazy loading of workspace libs follows Nx/Angular guidance .
---
##7Docker Artefacts
###7.1Dockerfiles
* **`backend/Dockerfile`** multistage .NET 9; singlefile publish; distroless runtime .
* **`frontend/Dockerfile`** Node 20 build Nginx static serve.
* Every plugin repo may include its own Dockerfile when shipping sidecars (e.g., custom scanner).
###7.2Compose Stacks
* **`docker-compose.yml`**
* Extends above with Redis 7 and Mongo 7 for small onprem installs.
* **`docker-compose.no-deps.yml`**
* backend, frontend, Trivy, Maven proxy.
* Assumes external Redis & Mongo.
* **`docker-compose.local.yml`**
* Build images from local source and bring up backend, frontend, Redis, Mongo, Trivy, Maven proxy for devloop.
Docker Compose override precedence matches official docs.
---
##8Performance Budget
| Flow | P95 target | Bottleneck |
|------|-----------:|-----------|
| SBOM fastpath | 5s | Redis queue depth (keep P99 <1ms) |
| Imageunpack | 10s | Trivy layer unpack. |
| Nightly rescan | 80SBOM/s | Runner CPU. |
---
##Change Log
| Version | Date | Notes |
|---------|------|-------|
| **v2.2** | 20250711 | Flat layout; stellaopsui naming; Dockerfiles & 3 Compose stacks; agents and localisation library. |
| v2.1 | 20250711 | First flatstructure draft. |
*(End of Module Specifications v2.2core)*

View File

@ -0,0 +1,329 @@
# API & CLI Reference
*Purpose* give operators and integrators a single, authoritative spec for REST/GRPC calls **and** firstparty CLI tools (`santech`, `zastava`, `stellopsctl`).
Everything here is *sourceoftruth* for generated Swagger/OpenAPI and the `--help` screens in the CLIs.
---
## 0 Quick Glance
| Area | Call / Flag | Notes |
| ------------------ | ------------------------------------------- | ------------------------------------------------------------------------------ |
| Scan entry | `POST /scan` | Accepts SBOM or image; sub5s target |
| Delta check | `POST /layers/missing` | <20ms reply; powers *delta SBOM* feature |
| Ratelimit / quota | | Headers **`XStellaQuotaRemaining`**, **`XStellaReset`** on every response |
| Policy I/O | `GET /policy/export`, `POST /policy/import` | YAML now; Rego coming |
| Policy lint | `POST /policy/validate` | Returns 200 OK if ruleset passes |
| Auth | `POST /connect/token` (OpenIddict) | Clientcredentials preferred |
| Health | `GET /healthz` | Simple liveness probe |
| Attestation * | `POST /attest` (TODO Q12026) | SLSA provenance + Rekor log |
| CLI flags | `--sbom-type` `--delta` `--policy-file` | Added to `santech` |
\* Marked **TODO** delivered after sixth month (kept on Feature Matrix To Do list).
---
## 1 Authentication
StellaOps uses **OAuth 2.0 / OIDC** (token endpoint mounted via OpenIddict).
```
POST /connect/token
ContentType: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=cibot&
client_secret=REDACTED&
scope=stella.api
```
Successful response:
```json
{
"access_token": "eyJraWQi...",
"token_type": "Bearer",
"expires_in": 3600
}
```
> **Tip**  pass the token via `Authorization: Bearer <token>` on every call.
---
## 2 REST API
###2.0Obtain / Refresh OfflineToken
```text
POST /token/offline
Authorization: Bearer <admintoken>
```
| Body field | Required | Example | Notes |
|------------|----------|---------|-------|
| `expiresDays` | no | `30` | Max 90 days |
```json
{
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
"expires": "20250817T00:00:00Z"
}
```
Token is signed with the backends private key and already contains
`"maxScansPerDay": 333`.
### 2.1 Scan  Upload SBOM **or** Image
```
POST /scan
```
| Param / Header | In | Required | Description |
| -------------------- | ------ | -------- | --------------------------------------------------------------------- |
| `XStellaSbomType` | header | no | `trivy-json-v2`, `spdx-json`, `cyclonedx-json`; omitted autodetect |
| `?threshold` | query | no | `low`, `medium`, `high`, `critical`; default **critical** |
| body | body | yes | *Either* SBOM JSON *or* Docker image tarball/upload URL |
Every successful `/scan` response now includes:
| Header | Example |
|--------|---------|
| `XStellaQuotaRemaining` | `129` |
| `XStellaReset` | `20250718T23:59:59Z` |
| `XStellaTokenExpires` | `20250817T00:00:00Z` |
**Response 200** (scan completed):
```json
{
"digest": "sha256:…",
"summary": {
"Critical": 0,
"High": 3,
"Medium": 12,
"Low": 41
},
"policyStatus": "pass",
"quota": {
"remaining": 131,
"reset": "2025-07-18T00:00:00Z"
}
}
```
**Response 202** queued; polling URL in `Location` header.
---
### 2.2 Delta SBOM Layer Cache Check
```
POST /layers/missing
ContentType: application/json
Authorization: Bearer <token>
```
```json
{
"layers": [
"sha256:d38b...",
"sha256:af45..."
]
}
```
**Response 200**<20ms target:
```json
{
"missing": [
"sha256:af45..."
]
}
```
Client then generates SBOM **only** for the `missing` layers and reposts `/scan`.
---
### 2.3 Policy Endpoints
| Method | Path | Purpose |
| ------ | ------------------ | ------------------------------------ |
| `GET` | `/policy/export` | Download live YAML ruleset |
| `POST` | `/policy/import` | Upload YAML or Rego; replaces active |
| `POST` | `/policy/validate` | Lint only; returns 400 on error |
| `GET` | `/policy/history` | Paginated change log (audit trail) |
```yaml
# Example import payload (YAML)
version: "1.0"
rules:
- name: Ignore Low dev
severity: [Low, None]
environments: [dev, staging]
action: ignore
```
Validation errors come back as:
```json
{
"errors": [
{
"path": "$.rules[0].severity",
"msg": "Invalid level 'None'"
}
]
}
```
---
### 2.4 Attestation (Planned  Q12026)
```
POST /attest
```
| Param | Purpose |
| ----------- | ------------------------------------- |
| body (JSON) | SLSA v1.0 provenance doc |
| | Signed + stored in local Rekor mirror |
Returns `202 Accepted` and `Location: /attest/{id}` for async verify.
---
### 2.5 Misc Endpoints
| Path | Method | Description |
| ---------- | ------ | ---------------------------- |
| `/healthz` | GET | Liveness; returns `"ok"` |
| `/metrics` | GET | Prometheus exposition (OTel) |
| `/version` | GET | Git SHA + build date |
---
## 3 FirstParty CLI Tools
### 3.1 `stellasantech`
> *Package SBOM + Scan + Exit code* designed for CI.
```
Usage: santech [OPTIONS] IMAGE_OR_SBOM
```
| Flag / Option | Default | Description |
| --------------- | ----------------------- | -------------------------------------------------- |
| `--server` | `http://localhost:8080` | API root |
| `--token` | *env `STELLA_TOKEN`* | Bearer token |
| `--sbom-type` | *auto* | Force `trivy-json-v2`/`spdx-json`/`cyclonedx-json` |
| `--delta` | `false` | Enable delta layer optimisation |
| `--policy-file` | *none* | Override server rules with local YAML/Rego |
| `--threshold` | `critical` | Fail build if level found |
| `--output-json` | *none* | Write raw scan result to file |
| `--wait-quota` | `true` | If 429 received, automatically wait `RetryAfter` and retry once. |
**Exit codes**
| Code | Meaning |
| ---- | ------------------------------------------- |
| 0 | Scan OK, policy passed |
| 1 | Vulnerabilities threshold OR policy block |
| 2 | Internal error (network etc.) |
---
### 3.2 `stellazastava`
> *Daemon / K8s DaemonSet* watch container runtime, push SBOMs.
Core flags (excerpt):
| Flag | Purpose |
| ---------------- | ---------------------------------- |
| `--mode` | `listen` (default) / `enforce` |
| `--filter-image` | Regex; ignore infra/busybox images |
| `--threads` | Worker pool size |
---
### 3.3 `stellopsctl`
> *Admin utility* policy snapshots, feed status, user CRUD.
Examples:
```
stellopsctl policy export > policies/backup-2025-07-14.yaml
stellopsctl feed refresh # force OSV merge
stellopsctl user add dev-team --role developer
```
---
## 4 Error Model
Uniform problemdetails object (RFC 7807):
```json
{
"type": "https://stella-ops.ru/probs/validation",
"title": "Invalid request",
"status": 400,
"detail": "Layer digest malformed",
"traceId": "00-7c39..."
}
```
---
## 5 Rate Limits
Default **40 requests / second / token**.
429 responses include `Retry-After` seconds header.
---
## 6 FAQ & Tips
* **Skip SBOM generation in CI** supply a *prebuilt* SBOM and add `?sbom-only=true` to `/scan` for <1s path.
* **Airgapped?** point `--server` to `http://oukgw:8080` inside the Offline Update Kit.
* **YAML vs Rego** YAML simpler; Rego unlocks timebased logic (see samples).
* **Cosign verify plugins** enable `SCANNER_VERIFY_SIG=true` env to refuse unsigned plugins.
---
## 7 Planned Changes (Beyond 6 Months)
These stay in *Feature Matrix → To Do* until design is frozen.
| Epic / Feature | API Impact Sketch |
| ---------------------------- | ---------------------------------- |
| **SLSA L1L3** attestation | `/attest` (see §2.4) |
| Rekor transparency log | `/rekor/log/{id}` (GET) |
| Plugin Marketplace metadata | `/plugins/market` (catalog) |
| Horizontal scaling controls | `POST /cluster/node` (add/remove) |
| Windows agent support | Update LSAPI to PDE, no API change |
---
## 8 References
* OpenAPI YAML `/openapi/v1.yaml` (served by backend)
* OAuth2 spec: <https://datatracker.ietf.org/doc/html/rfc6749>
* SLSA spec: <https://slsa.dev/spec/v1.0>
---
## 9 Changelog (truncated)
* **20250714** added *delta SBOM*, policy import/export, CLI `--sbom-type`.
* **20250712** initial public reference.
---

194
docs/10_PLUGIN_SDK_GUIDE.md Normal file
View File

@ -0,0 +1,194 @@
#10 · Plugin SDK Guide — **StellaOps**
*(v1.5  11Jul2025 · template install, no reload, IoC)*
---
##0Audience & Scope
Guidance for developers who extend StellaOps with schedule jobs, scanner adapters, TLS providers, notification channels, etc. Everything here is OSS; commercial variants simply ship additional signed plugins.
---
##1Prerequisites
| Tool | Min Version |
| ----------------------- | ----------------------------------------------------------------- |
| .NET SDK | 9.0.200 |
| **StellaOps templates** | install once via `bash dotnet new install StellaOps.Templates::*` |
| **Cosign** | 2.3+ — used to sign DLLs |
| xUnit | 2.6 |
| Docker CLI | only if your plugin shells out to containers |
---
##2Repository & Build Output
Every plugin is hosted in **`git.stellaops.org`**.
At publish time it must copy its signed artefacts to:
~~~text
src/backend/Stella.Ops.Plugin.Binaries/<MyPlugin>/
├── MyPlugin.dll
└── MyPlugin.dll.sig
~~~
The backend scans this folder on startup, verifies the **Cosign** signature, confirms the `[StellaPluginVersion]` gate, then loads the DLL inside an **isolated AssemblyLoadContext** to avoid dependency clashes
---
##3Project Scaffold
Generate with the installed template:
~~~bash
dotnet new stellaops-plugin-schedule \
-n MyPlugin.Schedule \
--output src
~~~
Result:
~~~text
src/
├─ MyPlugin.Schedule/
│ ├─ MyJob.cs
│ └─ MyPlugin.Schedule.csproj
└─ tests/
└─ MyPlugin.Schedule.Tests/
~~~
---
##4MSBuild Wiring
Add this to **`MyPlugin.Schedule.csproj`** so the signed DLL + `.sig` land in the canonical plugin folder:
~~~xml
<PropertyGroup>
<StellaPluginOut>$(SolutionDir)src/backend/Stella.Ops.Plugin.Binaries/$(MSBuildProjectName)</StellaPluginOut>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.Common\StellaOps.Common.csproj"
PrivateAssets="all" />
</ItemGroup>
<Target Name="CopyStellaPlugin" AfterTargets="Publish">
<MakeDir Directories="$(StellaPluginOut)" />
<Copy SourceFiles="$(PublishDir)$(AssemblyName).dll;$(PublishDir)$(AssemblyName).dll.sig"
DestinationFolder="$(StellaPluginOut)" />
</Target>
~~~
---
##5DependencyInjection Entrypoint
Backend autodiscovers the static method below:
~~~csharp
namespace StellaOps.DependencyInjection;
public static class IoCConfigurator
{
public static IServiceCollection Configure(this IServiceCollection services,
IConfiguration cfg)
{
services.AddSingleton<IJob, MyJob>(); // schedule job
services.Configure<MyPluginOptions>(cfg.GetSection("Plugins:MyPlugin"));
return services;
}
}
~~~
---
##6Schedule Plugins
###6.1 Minimal Job
~~~csharp
using StellaOps.Scheduling; // contract
[StellaPluginVersion("2.0.0")]
public sealed class MyJob : IJob
{
public async Task ExecuteAsync(CancellationToken ct)
{
Console.WriteLine("Hello from plugin!");
await Task.Delay(500, ct);
}
}
~~~
###6.2 Cron Registration
```csharp
services.AddCronJob<MyJob>("0 15 * * *"); // everyday
```
15:00
Cron syntax follows Hangfire rules 
##7Scanner Adapters
Implement IScannerRunner.
Register inside Configure:
```csharp
services.AddScanner<MyAltScanner>("alt"); // backend
```
selects by --engine alt
If the engine needs a sidecar container, include a Dockerfile in your repo and document resource expectations.
##8Packaging & Signing
```bash
dotnet publish -c Release -p:PublishSingleFile=true -o out
cosign sign --key $COSIGN_KEY out/MyPlugin.Schedule.dll # sign binary only
sha256sum out/MyPlugin.Schedule.dll > out/.sha256 # optional checksum
zip MyPlugin.zip out/* README.md
```
Unsigned DLLs are refused when StellaOps:Security:DisableUnsigned=false.
##9Deployment
```bash
docker cp MyPlugin.zip <backend>:/opt/plugins/ && docker restart <backend>
```
Check /health "plugins":["MyPlugin.Schedule@2.0.0"].
(Hotreload was removed to keep the core process simple and memorysafe.)
##10Configuration Patterns
| Need | Pattern |
| ------------ | --------------------------------------------------------- |
| Settings | Plugins:MyPlugin:* in appsettings.json. |
| Secrets | Redis secure:<plugin>:<key> (encrypted per TLS provider). |
| Dynamic cron | Implement ICronConfigurable; UI exposes editor. |
##11Testing & CI
| Layer | Tool | Gate |
| ----------- | -------------------------- | ------------------- |
| Unit | xUnit + Moq | ≥50% lines |
| Integration | Testcontainers run in CI | Job completes <5s |
| Style | dotnet | format 0 warnings |
Use the prebaked workflow in StellaOps.Templates as starting point.
##12Publishing to the Community Marketplace
Tag Git release pluginvX.Y.Z and attach the signed ZIP.
Submit a PR to stellaops/community-plugins.json with metadata & git URL.
On merge, the plugin shows up in the UI Marketplace.
##13Common Pitfalls
| Symptom | Root cause | Fix |
| ------------------- | -------------------------- | ------------------------------------------- |
| NotDetected | .sig missing | cosign sign |
| VersionGateMismatch | Backend 2.1 vs plugin 2.0 | Recompile / bump attribute |
| FileLoadException | Duplicate | StellaOps.Common Ensure PrivateAssets="all" |
| Redis | timeouts Large writes | Batch or use Mongo |

196
docs/11_DATA_SCHEMAS.md Normal file
View File

@ -0,0 +1,196 @@
#Data Schemas & Persistence Contracts
*Audience* backend developers, plugin authors, DB admins.
*Scope* describes **Redis**, **MongoDB** (optional), and ondisk blob shapes that power StellaOps.
---
##0Document Conventions
* **CamelCase** for JSON.
* All timestamps are **RFC 3339 / ISO 8601** with `Z` (UTC).
* `⭑` = planned but *not* shipped yet (kept on Feature Matrix “To Do”).
---
##1SBOMWrapper Envelope
Every SBOM blob (regardless of format) is stored on disk or in object storage with a *sidecar* JSON file that indexes it for the scanners.
#### 1.1 JSON Shape
```jsonc
{
"id": "sha256:417f…", // digest of the SBOM *file* itself
"imageDigest": "sha256:e2b9…", // digest of the original container image
"created": "2025-07-14T07:02:13Z",
"format": "trivy-json-v2", // NEW enum: trivy-json-v2 | spdx-json | cyclonedx-json
"layers": [
"sha256:d38b…", // layer digests (ordered)
"sha256:af45…"
],
"partial": false, // true => delta SBOM (only some layers)
"provenanceId": "prov_0291" // ⭑ link to SLSA attestation (Q12026)
}
```
*`format`* **NEW** added to support **multiple SBOM formats**.
*`partial`* **NEW** true when generated via the **delta SBOM** flow (§1.3).
#### 1.2 Filesystem Layout
```
blobs/
├─ 417f… # digest prefix
│   ├─ sbom.json # payload (any format)
│   └─ sbom.meta.json # wrapper (shape above)
```
> **Note** blob storage can point at S3, MinIO, or plain disk; driver plugins adapt.
####1.3Delta SBOM Extension
When `partial: true`, *only* the missing layers have been scanned.
Merging logic inside `scanning` module stitches new data onto the cached full SBOM in Redis.
---
##2Redis Keyspace
| Key pattern | Type | TTL | Purpose |
|-------------------------------------|---------|------|--------------------------------------------------|
| `scan:&lt;digest&gt;` | string | ∞ | Last scan JSON result (as returned by `/scan`) |
| `layers:&lt;digest&gt;` | set | 90d | Layers already possessing SBOMs (delta cache) |
| `policy:active` | string | ∞ | YAML **or** Rego ruleset |
| `quota:&lt;token&gt;` | string | *until next UTC midnight* | Pertoken scan counter for Free tier (333 scans). |
| `policy:history` | list | ∞ | Change audit IDs (see Mongo) |
| `feed:nvd:json` | string | 24h | Normalised feed snapshot |
| `locator:&lt;imageDigest&gt;` | string | 30d | Maps image digest → sbomBlobId |
| `metrics:…` | various | — | Prom / OTLP runtime metrics |
> **Delta SBOM** uses `layers:*` to skip work in <20ms.
> **Quota enforcement** increments `quota:<token>` atomically; when value333 the API returns **429**.
---
##3MongoDB Collections (Optional)
Only enabled when `MONGO_URI` is supplied (for longterm audit).
| Collection | Shape (summary) | Indexes |
|--------------------|------------------------------------------------------------|-------------------------------------|
| `sbom_history` | Wrapper JSON + `replaceTs` on overwrite | `{imageDigest}` `{created}` |
| `policy_versions` | `{_id, yaml, rego, authorId, created}` | `{created}` |
| `attestations` ⭑ | SLSA provenance doc + Rekor log pointer | `{imageDigest}` |
| `audit_log` | Fully rendered RFC 5424 entries (UI & CLI actions) | `{userId}` `{ts}` |
Schema detail for **policy_versions**:
```jsonc
{
"_id": "6619e90b8c5e1f76",
"yaml": "version: 1.0\nrules:\n - …",
"rego": null, // filled when Rego uploaded
"authorId": "u_1021",
"created": "2025-07-14T08:15:04Z",
"comment": "Imported via API"
}
```
---
##4Policy Schema (YAML v1.0)
Minimal viable grammar (subset of OSVSCHEMA ideas).
```yaml
version: "1.0"
rules:
- name: Block Critical
severity: [Critical]
action: block
- name: Ignore Low Dev
severity: [Low, None]
environments: [dev, staging]
action: ignore
expires: "2026-01-01"
- name: Escalate BDU High
sources: [BDU]
severity: [High, Critical]
action: escalate
```
Validation is performed by `policy:mapping.yaml` JSONSchema embedded in backend.
###4.1Rego Variant (Advanced  TODO)
*Accepted but stored asis in `rego` field.*
Evaluated via internal **OPA** sidecar once feature graduates from TODO list.
---
##5SLSA Attestation Schema 
Planned for Q12026 (kept here for early plugin authors).
```jsonc
{
"id": "prov_0291",
"imageDigest": "sha256:e2b9…",
"buildType": "https://slsa.dev/container/v1",
"builder": {
"id": "https://git.stella-ops.ru/ci/stella-runner@sha256:f7b7…"
},
"metadata": {
"invocation": {
"parameters": {"GIT_SHA": "f6a1…"},
"buildStart": "2025-07-14T06:59:17Z",
"buildEnd": "2025-07-14T07:01:22Z"
},
"completeness": {"parameters": true}
},
"materials": [
{"uri": "git+https://git…", "digest": {"sha1": "f6a1…"}}
],
"rekorLogIndex": 99817 // entry in local Rekor mirror
}
```
---
##6Validator Contracts
* For SBOM wrapper `ISbomValidator` (DLL plugin) must return *typed* error list.
* For YAML policies JSONSchema at `/schemas/policyv1.json`.
* For Rego OPA `opa eval --fail-defined` under the hood.
* For **Freetier quotas** `IQuotaService` integration tests ensure `quota:<token>` resets at UTC midnight and produces correct `RetryAfter` headers.
---
##7Migration Notes
1. **Add `format` column** to existing SBOM wrappers; default to `trivy-json-v2`.
2. **Populate `layers` & `partial`** via backfill script (ship with `stellopsctl migrate` wizard).
3. Policy YAML previously stored in Redis → copy to Mongo if persistence enabled.
4. Prepare `attestations` collection (empty) safe to create in advance.
---
##8Open Questions / Future Work
* How to deduplicate *identical* Rego policies differing only in whitespace?
* Embed *GOST 34.112018* digests when users enable Russian crypto suite?
* Should enterprise tiers share the same Redis quota keys or switch to JWT claim`tier != Free` bypass?
* Evaluate slidingwindow quota instead of strict daily reset.
* Consider ratelimit for `/layers/missing` to avoid bruteforce enumeration.
---
##9Change Log
| Date | Note |
|------------|--------------------------------------------------------------------------------|
| 20250714 | **Added:** `format`, `partial`, delta cache keys, YAML policy schema v1.0. |
| 20250712 | **Initial public draft** SBOM wrapper, Redis keyspace, audit collections. |
---

View File

@ -0,0 +1,167 @@
#12 - Performance Workbook
*Purpose* define **repeatable, datadriven** benchmarks that guard StellaOps core pledge:
> *“P95 vulnerability feedback in ≤5seconds.”*
---
##0Benchmark Scope
| Area | Included | Excluded |
|------------------|----------------------------------|---------------------------|
| SBOMfirst scan | Trivy engine w/ warmed DB | Full image unpack ≥300MB |
| Delta SBOM ⭑ | Missinglayer lookup & merge | Multiarch images |
| Policy eval ⭑ | YAML → JSON → rule match | Rego (until GA) |
| Feed merge | NVD JSON 20232025 | GHSA GraphQL (plugin) |
| Quota waitpath | 5s softwait, 60s hardwait behaviour | Paid tiers (unlimited) |
| API latency | REST `/scan`, `/layers/missing` | UI SPA calls |
⭑ = new in July2025.
---
##1Hardware Baseline (Reference Rig)
| Element | Spec |
|-------------|------------------------------------|
| CPU | 8vCPU (Intel IceLake equiv.) |
| Memory | 16GiB |
| Disk | NVMe SSD, 3GB/s R/W |
| Network | 1Gbit virt. switch |
| Container | Docker 25.0 + overlay2 |
| OS | Ubuntu 22.04LTS (kernel 6.8) |
*All P95 targets assume a **singlenode** deployment on this rig unless stated.*
---
##2Phase Targets & Gates
| Phase (ID) | Target P95 | Gate (CI) | Rationale |
|-----------------------|-----------:|-----------|----------------------------------------|
| **SBOM_FIRST** | ≤5s | `hard` | Core UX promise. |
| **IMAGE_UNPACK** | ≤10s | `soft` | Fallback path for legacy flows. |
| **DELTA_SBOM** ⭑ | ≤1s | `hard` | Needed to stay sub5s for big bases. |
| **POLICY_EVAL** ⭑ | ≤50ms | `hard` | Keeps gate latency invisible to users. |
| **QUOTA_WAIT** ⭑ | *soft*5s<br>*hard*60s | `hard` | Ensures graceful Freetier throttling. |
| **SCHED_RESCAN** | ≤30s | `soft` | Nightly batch not userfacing. |
| **FEED_MERGE** | ≤60s | `soft` | Offpeak cron @ 01:00. |
| **API_P95** | ≤200ms | `hard` | UI snappiness. |
*Gate* legend `hard`: break CI if regression>3×target,
`soft`: raise warning & issue ticket.
---
##3Test Harness
* **Runner** `perf/run.sh`, accepts `--phase` and `--samples`.
* **Metrics** Prometheus + `jq` extracts; aggregated via `scripts/aggregate.ts`.
* **CI** GitLab CI job *benchmark* publishes JSON to `benchartifacts/`.
* **Visualisation** Grafana dashboard *StellaPerf* (provisioned JSON).
> **Note** harness mounts `/var/cache/trivy` tmpfs to avoid disk noise.
---
##4Current Results (July2025)
| Phase | Samples | Mean (s) | P95 (s) | Target OK? |
|---------------|--------:|---------:|--------:|-----------:|
| SBOM_FIRST | 100 | 3.7 | 4.9 | ✅ |
| IMAGE_UNPACK | 50 | 6.4 | 9.2 | ✅ |
| **DELTA_SBOM**| 100 | 0.46 | 0.83 | ✅ |
| **POLICY_EVAL** | 1000 | 0.021 | 0.041 | ✅ |
| **QUOTA_WAIT** | 80 | 4.0* | 4.9* | ✅ |
| SCHED_RESCAN | 10 | 18.3 | 24.9 | ✅ |
| FEED_MERGE | 3 | 38.1 | 41.0 | ✅ |
| API_P95 | 20000 | 0.087 | 0.143 | ✅ |
*Data files:* `bench-artifacts/20250714/phasestats.json`.
---
##5ΔSBOM MicroBenchmark Detail
### 5.1 Scenario
1. Base image `python:3.12-slim` already scanned (all layers cached).
2. Application layer (`COPY . /app`) triggers new digest.
3. Santech lists **7** layers, backend replies *6 hit*, *1 miss*.
4. Builder scans **only 1 layer** (~9MiB, 217files) & uploads delta.
### 5.2 Key Timings
| Step | Time (ms) |
|---------------------|----------:|
| `/layers/missing` | 13 |
| Trivy single layer | 655 |
| Upload delta blob | 88 |
| Backend merge + CVE | 74 |
| **Total walltime** | **830ms** |
---
##6Quota WaitPath Benchmark Detail
###6.1Scenario
1. Freetier token reaches **scan #200** dashboard shows yellow banner.
###6.2 Key Timings
| Step | Time (ms) |
|------------------------------------|----------:|
| `/quota/check` Redis LUA INCR | 0.8 |
| Soft wait sleep (server) | 5000 |
| Hard wait sleep (server) | 60000 |
| Endtoend walltime (softhit) | 5003 |
| Endtoend walltime (hardhit) | 60004 |
---
##7Policy Eval Bench
### 7.1 Setup
* Policy YAML: **28** rules, mix severity & package conditions.
* Input: scan result JSON with **1026** findings.
* Evaluator: custom rules engine (Go structs → map lookups).
### 7.2 Latency Histogram
```
010ms ▇▇▇▇▇▇▇▇▇▇ 38%
1020ms ▇▇▇▇▇▇▇▇▇▇ 42%
2040ms ▇▇▇▇▇▇ 17%
4050ms ▇ 3%
```
P99=48ms. Meets 50ms gate.
---
##8Trend Snapshot
![Perf trend sparkline placeholder](perftrend.svg)
_Plot generated weekly by `scripts/updatetrend.py`; shows last 12 weeks P95 per phase._
---
##9Action Items
1. **Image Unpack** Evaluate zstd for layer decompress; aim to shave 1s.
2. **Feed Merge** Parallelise BDU XML parse (plugin) once stable.
3. **Rego Support** Prototype OPA sidecar; target ≤100ms eval.
4. **Concurrency** Stresstest 100rps on 4node Redis cluster (Q42025).
---
##10Change Log
| Date | Note |
|------------|-------------------------------------------------------------------------|
| 20250714 | Added ΔSBOM & Policy Eval phases; updated targets & current results. |
| 20250712 | First public workbook (SBOMfirst, imageunpack, feed merge). |
---

View File

@ -0,0 +1,209 @@
#13 · Release Engineering Playbook — StellaOps
A concise, automationfirst guide describing **how source code on `main` becomes a verifiably signed, airgapfriendly release**.
It is opinionated for offline usecases and supplychain security (SLSA ≥ level 2 today, aiming for level 3).
---
##0ReleasePhilosophy
* **Fast but fearless** every commit on `main` must be releasable; broken builds break the build, not the team.
* **Reproducible** anyone can rebuild byteidentical artefacts with a single `make release` offline.
* **Secure by default** every artefact ships with a SBOM, Cosign signature and (future) Rekor log entry.
* **Offlinefirst** all dependencies are vendored or mirrored into the internal registry; no Internet required at runtime.
---
##1Versioning & Branching
| Branch | Purpose | Autopublish? |
| ------------- | ------------------------------ | --------------------------------------- |
| `main` | Alwaysgreen development trunk | `nightly-*` images |
| `release/X.Y` | Stabilise a minor line | `stella:X.Y-rcN` |
| Tags | `X.Y.Z` = SemVer | `stella:X.Y.Z`, OUK tarball, Helm chart |
* **SemVer** MAJOR for breaking API/CLI changes, MINOR for features, PATCH for fixes.
* Release tags are **signed** (`git tag -s`) with the StellaOps GPG key (`0x90C4…`).
---
##2CI/CD Overview (GitLabCI + GitLab Runner)
```mermaid
graph LR
A[push / MR] --> Lint
Lint --> Unit
Unit --> Build
Build --> Test-Container
Test-Container --> SBOM
SBOM --> Sign
Sign --> Publish
Publish --> E2E
Publish --> Notify
```
###Pipeline Stages
| Stage | Key tasks |
| ------------------ | ------------------------------------------------------------------------------------------------ |
| **Lint** | ESLint, golangcilint, hadolint, markdownlint. |
| **Unit** | `dotnet test`, `go test`, Jest UI tests. |
| **Quota unittests 🏷** | Validate QuotaService logic: reset at UTC, 5s vs 60s waits, header correctness. |
| **Build** | Multiarch container build (`linux/amd64`, `linux/arm64`) using **BuildKit** + `--provenance` 📌. |
| **TestContainer** | Spin up compose file, run smoke APIs. |
| **SBOM** 📌 | Invoke **StellaOps.SBOMBuilder** to generate SPDX JSON + attach `.sbom` label to image. |
| **Sign** | Sign image with **Cosign** (`cosign sign --key cosign.key`). |
| **Publish** | Push to `registry.git.stella-ops.ru`. |
| **E2E** | Kindbased Kubernetes test incl. Zastava DaemonSet; verify sub5s scan SLA. |
| **Notify** | Report to Mattermost & GitLab Slack app. |
| **OfflineToken** | Call `JwtIssuer.Generate(exp=30d)` → store `client.jwt` artefact → attach to OUK build context |
*All stages run in parallel where possible; max walltime <15min.*
---
##3Container Image Strategy
| Image | Registry Tag | Contents |
| ------------------------------ | --------------------------- | ---------------------------------------------------------------------- |
| **backend** | `stella/backend:{ver}` | ASP.NET API, plugin loader. |
| **ui** | `stella/ui:{ver}` | Prebuilt Angular SPA. |
| **runner-trivy** | `stella/runner-trivy:{ver}` | Trivy CLI + SPDX/CycloneDX 🛠. |
| **runner-grype** | `stella/runner-grype:{ver}` | Optional plugin scanner. |
| **🏷StellaOps.Registry** 📌 | `stella/registry:{ver}` | Scratch image embedding Docker Registryv2 + Cosign policy controller. |
| **🏷StellaOps.MutePolicies** 📌 | `stella/policies:{ver}` | Sidecar serving policy bundles. |
| **🏷StellaOps.Attestor** 📌 | `stella/attestor:{ver}` | SLSA provenance & Rekor signer (future). |
*Images are **`--label org.opencontainers.image.source=git.stella-ops.ru`** and include SBOMs generated at build time.*
---
##4📌Offline Update Kit (OUK) Build & Distribution
**Purpose** deliver updated CVE feeds & Trivy DB to airgapped clusters.
###4.1CLI Tool
*Go binary `ouk` lives in `tools/ouk/`.*
```sh
ouk fetch \
--nvd --osv \
--trivy-db --date $(date -I) \
--output ouk-$(date +%Y%m%d).tar.gz \
--sign cosign.key
```
###4.2PipelineHook
* Runs on **first Friday** each month (cron).
* Generates tarball, signs it, uploads to **GitLab Release asset**.
* SHA256 + signature published alongside.
###4.3ActivationFlow (runtime)
1. Admin uploads `.tar.gz` via **UI → Settings → Offline Updates (OUK)**.
2. Backend verifies Cosign signature & digest.
3. Files extracted into `var/lib/stella/db`.
4. Redis caches invalidated; Dashboard “Feed Age” ticks green.
5. Audit event `ouk_update` stored.
### 4.4 Token Detail
client.jwt placed under /root/ inside the tarball.
CI job fails if token expiry <29days (guard against stale caches).
---
##5Artifact Signing & Transparency
| Artefact | Signer | Tool |
| ------------ | --------------- | --------------------- |
| Git tags | GPG (`0x90C4…`) | `git tag -s` |
| Containers | Cosign key pair | `cosign sign` |
| Helm Charts | prov file | `helm package --sign` |
| OUK tarballs | Cosign | `cosign sign-blob` |
**Rekor** integration is **TODO** once the internal Rekor mirror is online (`StellaOpsAttestor`) a postpublish job will submit transparency log entries.
---
##6Release Checklist
1. CI pipeline green.
2. Bump `VERSION` file.
3. Tag `git tag -s X.Y.Z -m "Release X.Y.Z"` & push.
4. GitLab CI autopublishes images & charts.
5. Draft GitLab **Release Notes** using `tools/release-notes-gen`.
6. Verify SBOM attachment with `stella sbom verify stella/backend:X.Y.Z`.
7. Smoketest OUK tarball in offline lab.
8. Announce in `#stella-release` Mattermost channel.
---
##7Hotfix Procedure
* Branch from latest tag `hotfix/X.Y.Z+1-hf1`.
* Apply minimal patch, add regression test.
* CI pipeline (with reduced stages) must pass.
* Tag `X.Y.Z+1`.
* Publish only container + Helm chart; OUK not rebuilt.
* Cherrypick back to `main`.
---
##8Deprecation & EndofLife Policy
| Feature | Deprecation notice | Removal earliest |
| ------------------------ | ------------------ | ---------------- |
| Legacy CSV policy import | 20251001 | 20260401 |
| Docker v1 Registry auth | 20251201 | 20260601 |
| Inimage Trivy DB | 20251215 | 20260315 |
*At least 6 months notice; removal requires major version bump.*
---
##9📌NonCommercial Usage Rules (English canonical)
1. **Free for internal security assessments** (company or personal).
2. **SaaS resale / rehosting prohibited** without prior written consent (AGPL §13).
3. If you distribute a fork with UI or backend modifications **you must**:
* Publish the complete modified source code.
* Retain the original StellaOps attribution in UI footer and CLI `--version`.
4. All thirdparty dependencies remain under their respective licences (MIT, Apache2.0, ISC, BSD).
5. Deployments in stateregulated or classified environments must obey **ФЗ187** export rules.
---
##10Best Practices Snapshot 📌
* **SBOMperimage** attach at build time; store as OCI artifact for supplychain introspection.
* **Provenance flag** (`--provenance=true`) in BuildKit fulfils SLSA 2 requirement.
* Use **multiarch, reproducible builds** (`SOURCE_DATE_EPOCH` pins timestamps).
* All pipelines enforce **Signedoffby (DCO)**; CI fails if trailer missing.
* `cosign policy` ensures only images signed by the project key run in production.
---
##11Contributing to Release Engineering
* Fork & create MR to `infra/release-*`.
* All infra changes require green **`integration-e2e-offline`** job.
* Discuss larger infra migrations in `#sig-release` Mattermost; decisions recorded in `ADR/` folder.
---
##12Change Log (highlevel)
| Version | Date | Note |
| ------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| v2.1 | 20250715 | Added OUK build/publish pipeline, internal registry image (`StellaOps.Registry`), noncommercial usage rules extraction, SBOM stage, BuildKit provenance. |
| v2.0 | 20250712 | Initial opensourcing of Release Engineering guide. |
| v1.1 | 20250709 | Fixed inner fencing; added retention policy |
| v1.0 | 20250709 | Initial playbook |
---
*(End of Release Engineering Playbook v1.1)*

View File

@ -0,0 +1,112 @@
#14 · Glossary of Terms —StellaOps
*(v1.0  12Jul2025 · first real content, replaces placeholder v0.1)*
---
###0Purpose
A concise, singlepage **“what does that acronym actually mean?”** reference for
developers, DevOps engineers, IT managers and auditors who are new to the
StellaOps documentation set.
*If you meet a term in any StellaOps doc that is **not** listed here, please
open a PR and append it alphabetically.*
---
##A C
| Term | Short definition | Links / notes |
|------|------------------|---------------|
| **ADR** | *Architecture Decision Record* lightweight Markdown file that captures one irreversible design decision. | ADR template lives at `/docs/adr/` |
| **AIRE** | *AI Risk Evaluator* optional Plus/Pro plugin that suggests mute rules using an ONNX model. | Commercial feature |
| **AzurePipelines** | CI/CD service in Microsoft Azure DevOps. | Recipe in Pipeline Library |
| **BDU** | Russian (FSTEC) national vulnerability database: *База данных уязвимостей*. | Merged with NVD by FeedMerger |
| **BuildKit** | Modern Docker build engine with caching and concurrency. | Needed for layer cache patterns |
| **CI** | *Continuous Integration* automated build/test pipeline. | Stella integrates via CLI |
| **Cosign** | Opensource Sigstore tool that signs & verifies container images **and files**. | Images & OUK tarballs |
| **CWV / CLS** | *Core Web Vitals* metric Cumulative Layout Shift. | UI budget ≤0.1 |
| **CycloneDX** | Open SBOM(BOM) standard alternative to SPDX. | Planned report format plugin |
---
##D G
| Term | Definition | Notes |
|------|------------|-------|
| **Digest (image)** | SHA256 hash uniquely identifying a container image or layer. | Pin digests for reproducible builds |
| **DockerinDocker (DinD)** | Running Docker daemon inside a CI container. | Used in GitHub / GitLab recipes |
| **DTO** | *Data Transfer Object* C# record serialised to JSON. | Schemas in doc 11 |
| **FeedMerger** | Background job that merges NVD JSON and (optionally) BDU XML into Redis. | Cron default `01* * *` |
| **FSTEC** | Russian regulator issuing SOBIT certificates. | Pro GA target |
| **Gitea** | Selfhosted Git service mirrors GitHub repo. | OSS hosting |
| **GOST TLS** | TLS ciphersuites defined by Russian GOST R 34.102012 / 34.112012. | Provided by `OpenSslGost` or CryptoPro |
| **Grype** | Alternative OSS vulnerability scanner; can be hotloaded as plugin. | Scanner interface `IScannerRunner` |
---
##H L
| Term | Definition | Notes |
|------|------------|-------|
| **Helm** | Kubernetes package manager (charts). | Beta chart under `/charts/core` |
| **Hotload** | Runtime discovery & loading of plugins **without restart**. | Cosignsigned DLLs |
| **Hyperfine** | CLI microbenchmark tool used in Performance Workbook. | Outputs CSV |
| **JWT** | *JSON Web Token* bearer auth token issued by OpenIddict. | Scope `scanner`, `admin`, `ui` |
| **K3s / RKE2** | Lightweight Kubernetes distributions (Rancher). | Supported in K8s guide |
| **Kubernetes NetworkPolicy** | K8s resource controlling pod traffic. | Redis/Mongo isolation |
---
##M O
| Term | Definition | Notes |
|------|------------|-------|
| **Mongo (optional)** | Document DB storing >180day history and audit logs. | Off by default in Core |
| **Mute rule** | JSON object that suppresses specific CVEs until expiry. | Schema `mute-rule1.json` |
| **NVD** | USbased *National Vulnerability Database*. | Primary CVE source |
| **ONNX** | Portable neuralnetwork model format; used by AIRE. | Runs inprocess |
| **OpenIddict** | .NET library that implements OAuth2 / OIDC in Stella backend. | Embedded IdP |
| **OUK** | *Offline Update Kit* signed tarball with images + feeds for airgap. | Admin guide #24 |
| **OTLP** | *OpenTelemetry Protocol* exporter for traces & metrics. | `/metrics` endpoint |
---
##P S
| Term | Definition | Notes |
|------|------------|-------|
| **P95** | 95thpercentile latency metric. | Target ≤5s SBOM path |
| **PDF SAR** | *Security Assessment Report* PDF produced by Pro edition. | Cosignsigned |
| **Plugin** | Hotloadable DLL implementing a Stella contract (`IScannerRunner`, `ITlsProvider`, etc.). | Signed with Cosign |
| **Problem Details** | RFC7807 JSON error format returned by API. | See API ref §0 |
| **Redis** | Inmemory datastore used for queue + cache. | Port 6379 |
| **Rekor** | Sigstore transparency log; future work for signature anchoring. | Roadmap P4 |
| **RPS** | *Requests Per Second*. | Backend perf budget 40rps |
| **SBOM** | *Software Bill of Materials* inventory of packages in an image. | Trivy JSON v2 |
| **Santech** | Lightweight cli that sends SBOM for vulnerability scanning | |
| **Seccomp** | Linux syscall filter JSON profile. | Backend shipped nonroot |
| **SLA** | *ServiceLevel Agreement* 24h / 1ticket for Pro. | SRE runbook |
| **Span<T>** | .NET reflike struct for zeroalloc slicing. | Allowed with benchmarks |
| **Styker.NET** | Mutation testing runner used on critical libs. | Coverage ≥60% |
---
##T Z
| Term | Definition | Notes |
|------|------------|-------|
| **Trivy** | OSS CVE scanner powering the default `IScannerRunner`. | CLI pinned 0.64 |
| **Trivysrv** | Longrunning Trivy server exposing gRPC API; speeds up remote scans. | Variant A |
| **UI tile** | Dashboard element showing live metric (scans today, feed age, etc.). | Angular Signals |
| **WebSocket** | Fullduplex channel (`/ws/scan`, `/ws/stats`) for UI realtime. | Used by tiles |
| **Zastava** | Lightweight agent that inventories running containers and can enforce kills. | |
---
###11Change log
| Version | Date | Notes |
|---------|------|-------|
| **v1.0** | 20250712 | First populated glossary 52 terms covering Core docs. |
*(End of Glossary v1.0)*

234
docs/15_UI_GUIDE.md Normal file
View File

@ -0,0 +1,234 @@
# 15 - PragmaticUI Guide --- **StellaOps**
#StellaOps Web UI
A fast, modular singlepage application for controlling scans, policies, offline updates and platformwide settings.
Built for subsecond feedback, darkmode by default, and **no external CDNs** everything ships inside the anonymous internal registry.
---
##0FastFacts
| Aspect | Detail |
| ----------------- | -------------------------------------------------------------------------- |
| Tech Stack | **Angular17** + Vite dev server |
| Styling | **Tailwind CSS** |
| State | Angular Signals + RxJS |
| API Client | OpenAPI v3 generated services (Axios) |
| Auth | OAuth2/OIDC (tokens from backend or external IdP) |
| i18n | JSON bundles **`/locales/{lang}.json`** (English, Russian shipped) |
| Offline Updates 📌 | UI supports “OUK” tarball upload to refresh NVD / Trivy DB when airgapped |
| Build Artifacts | (`ui/dist/`) pushed to `registry.git.stella-ops.ru/ui:${SHA}` |
---
##1Navigation Map
```
Dashboard
└─ Scans
├─ Active
├─ History
└─ Reports
└─ Policies 📌
├─ Editor (YAML / Rego) 📌
├─ Import / Export 📌
└─ History
└─ Settings
├─ SBOM Format 📌
├─ Registry 📌
├─ Offline Updates (OUK) 📌
├─ Themes (Light / Dark / System) 📌
└─ Advanced
└─ Plugins 🛠
└─ Help / About
```
*The **Offline Updates (OUK)** node under **Settings** is new.*
---
##2Technology Overview
###2.1Build & Deployment
1. `npm i && npm build` → generates `dist/` (~2.1MB gzip).
2. A CI job tags and pushes the artifact as `ui:${GIT_SHA}` to the internal registry.
3. Backend serves static assets from `/srv/ui` (mounted from the image layer).
_No external fonts or JS true offline guarantee._
###2.2Runtime Boot
1. **AppConfigService** pulls `/api/v1/config/ui` (contains feature flags, default theme, enabled plugins).
2. Locale JSON fetched (`/locales/{lang}.json`, falls back to `en`).
3. Root router mounts lazyloaded **feature modules** in the order supplied by backend this is how future route plugins inject pages without forking the UI.
---
##3Feature WalkThroughs
###3.1Dashboard RealTime Status
* **ΔSBOM heatmap** 📌 shows how many scans used delta mode vs. full unpack.
* “Feed Age” tile turns **orange** if NVD feed is older than 24h; reverts after an **OUK** upload 📌.
* Live WebSocket updates for scans in progress (SignalR channel).
* **Quota Tile** shows **Scans Today /333**; turns yellow at 200,
red at 333.
* **Token Expiry Tile** shows days left on *client.jwt* (offline only);
turns orange at <7days.
###3.2Scans Module
| View | What you can do |
| ----------- | ------------------------------------------------------------------------------------------------- |
| **Active** | Watch progress bar (ETA 5s) newly added **Format** and **Δ** badges appear beside each item. |
| **History** | Filter by repo, tag, policy result (pass/block/softfail). |
| **Reports** | Click row HTML or PDF report rendered by backend (`/report/{digest}/html`). |
###3.3📌Policies Module (new)
*Embedded **Monaco** editor with YAML + Rego syntax highlighting.*
| Tab | Capability |
| ------------------- | ------------------------------------------------------------------------------------------------ |
| **Editor** | Write or paste `scan-policy.yaml` or inline Rego snippet. Schema validation shown inline. |
| **Import / Export** | Buttons map to `/policy/import` and `/policy/export`. Accepts `.yaml`, `.rego`, `.zip` (bundle). |
| **History** | Immutable audit log; diff viewer highlights rule changes. |
####3.3.1YAML → Rego Bridge
If you paste YAML but enable **Strict Mode** (toggle), backend converts to Rego under the hood, stores both representations, and shows a sidebyside diff.
###3.4📌Settings Enhancements
| Setting | Details |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **SBOM Format** | Dropdown *Trivy JSON*, *SPDX JSON*, *CycloneDX JSON*. |
| **Registry** | Displays pull URL (`registry.git.stella-ops.ru`) and Cosign key fingerprint. |
| **Offline Updates (OUK)** 📌 | Upload **`ouk*.tar.gz`** produced by the Offline Update Kit CLI. Backend unpacks, verifies SHA256 checksum & Cosign signature, then reloads Redis caches without restart. |
| **Theme** | Light, Dark, or Auto (system). |
####3.4.1OUK Upload Screen 📌
*Page path:* **SettingsOffline Updates (OUK)**
*Components:*
1. **Drop Zone** drag or select `.tar.gz` (max 1GB).
2. **Progress Bar** streaming upload with chunked HTTP.
3. **Verification Step** backend returns status:
* *Signature valid*
* *Digestmismatch*
4. **Feed Preview** table shows *NVD date*, *OUI source build tag*, *CVE count delta*.
5. **Activate** button issues `/feeds/activate/{id}`; on success the Dashboard Feed Age tile refreshes to green.
6. **History List** previous OUK uploads with user, date, version; supports rollback.
*All upload actions are recorded in the PoliciesHistory audit log as type `ouk_update`.*
###3.5Plugins Panel 🛠 (ships after UI modularisation)
Lists discovered UI plugins; each can inject routes/panels. Toggle on/off without reload.
###3.6Settings**Quota & Tokens** (new)
* View current **ClientJWT claims** (tier, maxScansPerDay, expiry).
* **Generate Offline Token** adminonly button POST `/token/offline` (UI wraps the API).
* Upload new token file for manual refresh.
---
##4i18n & l10n
* JSON files under `/locales`.
* Russian (`ru`) ships firstclass, translated security terms align with **GOST RISO/IEC 270022020**.
* Offline Update Kit surfaces as **Оффлайнобновление базы уязвимостей”** in Russian locale.
* Community can add locales by uploading a new JSON via Plugins Panel once 🛠 ships.
---
##5Accessibility
* WCAG 2.1 AA conformance targeted.
* All color pairs pass contrast (checked by `vite-plugin-wcag`).
* Keyboard navigation fully supported; focus outlines visible in both themes.
---
##6Theming 📌
| Layer | How to change |
| --------------- | ------------------------------------------------------------ |
| Tailwind | Palette variables under `tailwind.config.js > theme.colors`. |
| Runtime toggle | Stored in `localStorage.theme`, synced across tabs. |
| Plugin override | Future route plugins may expose additional palettes 🛠. |
---
##7Extensibility Hooks
| Area | Contract | Example |
| ------------- | ---------------------------------------- | ---------------------------------------------- |
| New route | `window.stella.registerRoute()` | Secrets scanner plugin adds `/secrets` page. |
| External link | `window.stella.addMenuLink(label, href)` | Docs link opens corporate Confluence. |
| Theme | `window.stella.registerTheme()` | Highcontrast palette for accessibility. |
---
##8RoadMap Tags
| Feature | Status |
| ------------------------- | ------ |
| Policy Editor (YAML) | |
| Inline Rego validation | 🛠 |
| OUK Upload UI | |
| Plugin Marketplace UI | 🚧 |
| SLSA Verification banner | 🛠 |
| Rekor Transparency viewer | 🚧 |
---
##9NonCommercial Usage Rules 📌
*(Extracted & harmonised from the Russian UI help page so that English docs remain licencecomplete.)*
1. **Free for internal security assessments.**
2. Commercial resale or SaaS rehosting **prohibited without prior written consent** under AGPL§13.
3. If you distribute a fork **with UI modifications**, you **must**:
* Make the complete source code (including UI assets) publicly available.
* Retain original project attribution in footer.
4. All dependencies listed in `ui/package.json` remain under their respective OSS licences (MIT, Apache2.0, ISC).
5. Use in governmentclassified environments must comply with **ФЗ187** export rules; consult your legal team.
---
##10Troubleshooting Tips
| Symptom | Cause | Remedy |
| ----------------------------------- | ----------------------------------- | ----------------------------------------------------------------- |
| **White page** after login | `ui/dist/` hash mismatch | Clear browser cache; backend autobusts on version change. |
| Policy editor shows Unknown field | YAML schema drift | Sync your policy file to latest sample in *SettingsTemplates*. |
| **OUK upload fails** at 99% | Tarball built with outdated OUK CLI | Upgrade CLI (`ouk --version`) and rebuild package. |
| Icons look broken in Safari | *SVG `mask` unsupported* | Use Safari17+ or switch to PNG icon set in Settings > Advanced. |
---
##11Contributing
* Run `npm dev` and open `http://localhost:5173`.
* Ensure `ng lint` and `ng test` pass before PR.
* Sign the **DCO** in your commit footer (`Signed-off-by`).
---
##12Change Log
| Version | Date | Highlights |
| ------- | ---------- |
| v2.4 | 20250715 | **Added full OUK Offline Update upload flow** navigation node, Settings panel, dashboard linkage, audit hooks. |
| v2.3 | 20250714 | Added Policies module, SBOM Format & Registry settings, theming toggle, ΔSBOM indicators, extracted noncommercial usage rules. |
| v2.2 | 20250712 | Added user tips/workflows, CI notes, DevSecOps section, troubleshooting, screenshots placeholders. |
| v2.1 | 20250712 | Removed PWA/Serviceworker; added oidcclientts; simplified roadmap |
| v2.0 | 20250712 | Accessibility, Storybook, perf budgets, security rules |
| v1.1 | 20250711 | Original OSSonly guide |
(End of PragmaticUI Guide v2.2)

View File

@ -0,0 +1,186 @@
# 17 · Security Hardening Guide — **StellaOps**
*(v2.0  12Jul2025)*
> **Audience** — Sitereliability and platform teams deploying **the opensource Core** in production or restricted networks.
---
## 0TableofContents
1. Threat model (summary)
2. HostOS baseline
3. Container & runtime hardening
4. Networkplane guidance
5. Secrets & key management
6. Image, SBOM & plugin supplychain controls
7. Logging, monitoring & audit
8. Update & patch strategy
9. Incidentresponse workflow
10. Pentesting & continuous assurance
11. Contacts & vulnerability disclosure
12. Change log
---
## 1Threat model (summary)
| Asset | Threats | Mitigations |
| -------------------- | --------------------- | ---------------------------------------------------------------------- |
| SBOMs & scan results | Disclosure, tamper | TLSintransit, readonly Redis volume, RBAC, Cosignverified plugins |
| Backend container | RCE, codeinjection | Distroless image, nonroot UID, readonly FS, seccomp + `CAP_DROP:ALL` |
| Update artefacts | Supplychain attack | Cosignsigned images & SBOMs, enforced by admission controller |
| Admin credentials | Phishing, brute force | OAuth 2.0 with 12h token TTL, optional mTLS |
---
## 2HostOS baseline checklist
| Item | Recommended setting |
| ------------- | --------------------------------------------------------- |
| OS | Ubuntu22.04LTS (kernel5.15) or Alma9 |
| Patches | `unattendedupgrades` or vendorequivalent enabled |
| Filesystem | `noexec,nosuid` on `/tmp`, `/var/tmp` |
| Docker Engine | v24.*, API socket rootowned (`0660`) |
| Auditd | Watch `/etc/docker`, `/usr/bin/docker*` and Compose files |
| Time sync | `chrony` or `systemdtimesyncd` |
---
## 3Container & runtime hardening
### 3.1Docker Compose reference (`compose-core.yml`)
```yaml
services:
backend:
image: ghcr.io/stellaops/backend:1.5.0
user: "101:101" # nonroot
read_only: true
security_opt:
- "no-new-privileges:true"
- "seccomp:./seccomp-backend.json"
cap_drop: [ALL]
tmpfs:
- /tmp:size=64m,exec,nosymlink
environment:
- ASPNETCORE_URLS=https://+:8080
- TLSPROVIDER=OpenSslGost
depends_on: [redis]
networks: [core-net]
healthcheck:
test: ["CMD", "wget", "-qO-", "https://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 5
redis:
image: redis:7.2-alpine
command: ["redis-server", "--requirepass", "${REDIS_PASS}", "--rename-command", "FLUSHALL", ""]
user: "redis"
read_only: true
cap_drop: [ALL]
tmpfs:
- /data
networks: [core-net]
networks:
core-net:
driver: bridge
```
No dedicated “Redis” or “Mongo” subnets are declared; the single bridge network suffices for the default stack.
### 3.2Kubernetes deployment highlights
Use a separate NetworkPolicy that only allows egress from backend to Redis :6379.
securityContext: runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation: false, drop all capabilities.
PodDisruptionBudget of minAvailable: 1.
Optionally add CosignVerified=true label enforced by an admission controller (e.g. Kyverno or Connaisseur).
## 4Networkplane guidance
| Plane | Recommendation |
| ------------------ | -------------------------------------------------------------------------- |
| Northsouth | Terminate TLS 1.2+ (OpenSSLGOST default). Use LetsEncrypt or internal CA. |
| Eastwest | Compose bridge or K8s ClusterIP only; no public Redis/Mongo ports. |
| Ingress controller | Limit methods to GET, POST, PATCH (no TRACE). |
| Ratelimits | 40 rps default; tune ScannerPool.Workers and ingress limitreq to match. |
## 5Secrets & key management
| Secret | Storage | Rotation |
| --------------------------------- | ---------------------------------- | ----------------------------- |
| **ClientJWT (offline)** | `/var/lib/stella/tokens/client.jwt` (root:600) | **30days** provided by each OUK |
| REDIS_PASS | Docker/K8s secret | 90days |
| OAuth signing key | /keys/jwt.pem (readonly mount) | 180days |
| Cosign public key | /keys/cosign.pub baked into image; | change on every major release |
| Trivy DB mirror token (if remote) | Secret + readonly | 30days |
Never bake secrets into images; always inject at runtime.
> **Operational tip:** schedule a cron reminding ops 5days before
> `client.jwt` expiry. The backend also emits a Prometheus metric
> `stella_quota_token_days_remaining`.
## 6Image, SBOM & plugin supplychain controls
* Images — Pull by digest not latest; verify:
```bash
cosign verify ghcr.io/stellaops/backend@sha256:<DIGEST> \
--key https://stella-ops.org/keys/cosign.pub
```
* SBOM — Each release ships an SPDX file; store alongside images for audit.
* Thirdparty plugins — Place in /plugins/; backend will:
* Validate Cosign signature.
* Check [StellaPluginVersion("major.minor")].
* Refuse to start if Security.DisablePluginUnsigned=false (default).
## 7Logging, monitoring & audit
| Control | Implementation |
| ------------ | ----------------------------------------------------------------- |
| Log format | Serilog JSON; ship via FluentBit to ELK or Loki |
| Metrics | Prometheus /metrics endpoint; default Grafana dashboard in infra/ |
| Audit events | Redis stream audit; export daily to SIEM |
| Alert rules | Feed age 48h, P95 walltime>5s, Redis used memory>75% |
## 8Update & patch strategy
| Layer | Cadence | Method |
| -------------------- | -------------------------------------------------------- | ------------------------------ |
| Backend & CLI images | Monthly or CVEdriven docker pull + docker compose up -d |
| Trivy DB | 24h cron via FeedMerger | configurable (FeedMerger.Cron) |
| Docker Engine | vendor LTS | distro package manager |
| Host OS | security repos enabled | unattendedupgrades |
## 9Incidentresponse workflow
* Detect — PagerDuty alert from Prometheus or SIEM.
* Contain — Stop affected Backend container; isolate Redis RDB snapshot.
* Eradicate — Pull verified images, redeploy, rotate secrets.
* Recover — Restore RDB, replay SBOMs if history lost.
* Review — Postmortem within 72h; create followup issues.
* Escalate P1 incidents to <security@stellaops.org> (24×7).
## 10Pentesting & continuous assurance
| Control | Frequency | Tool |
|-------------------|-------------------|
| OWASP | ZAP baseline | Each merge to main GitHub Action zap-baseline-scan |
| Dependency scanning | Pull request | Trivy FS + GitHub Dependabot |
| External redteam | Annual or before GA | 3rdparty CRESTaccredited vendor |
## 11Vulnerability disclosure & contact
* Preferred channel: security@stellaops.org (GPG key on website).
* Coordinated disclosure reward: public credit and swag (no monetary bounty at this time).
## 12Change log
| Version | Date | Notes |
| ------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
| v2.0 | 20250712 | Full overhaul: hostOS baseline, supplychain signing, removal of unnecessary subnets, rolebased contact email, K8s guidance. |
| v1.1 | 20250709 | Minor fence fixes. |
| v1.0 | 20250709 | Original draft. |

169
docs/18_CODING_STANDARDS.md Normal file
View File

@ -0,0 +1,169 @@
# 18 · Coding Standards & Contributor Guide — **StellaOps**
*(v2.0  12Jul2025 · supersedes v1.0)*
> **Audience** — Anyone sending a pullrequest to the opensource Core.
> **Goal** — Keep the codebase smallfiled, pluginfriendly, DIconsistent, and instantly readable.
---
## 0Why read this?
* Cuts review time → quicker merges.
* Guarantees code is **hotloadsafe** for runtime plugins.
* Prevents style churn and merge conflicts.
---
## 1Highlevel principles
1. **SOLID first** especially Interface & Dependency Inversion.
2. **100line rule** any file >100 physical lines must be split or refactored.
3. **Contractlevel ownership** public abstractions live in lightweight *Contracts* libraries; impl classes live in runtime projects.
4. **Single Composition Root** all DI wiring happens in **`StellaOps.Web/Program.cs`** and in each plugins `IoCConfigurator`; nothing else calls `IServiceCollection.BuildServiceProvider`.
5. **No Service Locator** constructor injection only; static `ServiceProvider` is banned.
6. **Failfast startup** configuration validated before the webhost listens.
7. **Hotload compatible** no static singletons that survive plugin unload; avoid `Assembly.LoadFrom` outside the builtin plugin loader.
---
## 2Repository layout (flat, July2025)**
```text
src/
├─ backend/
│ ├─ StellaOps.Web/ # ASP.NET host + composition root
│ ├─ StellaOps.Common/ # Serilog, Result<T>, helpers
│ ├─ StellaOps.Contracts/ # DTO + interface contracts (no impl)
│ ├─ StellaOps.Configuration/ # Options + validation
│ ├─ StellaOps.Localization/
│ ├─ StellaOps.PluginLoader/ # Cosign verify, hotload
│ ├─ StellaOps.Scanners.Trivy/ # Firstparty scanner
│ ├─ StellaOps.TlsProviders.OpenSsl/
│ └─ … (additional runtime projects)
├─ plugins-sdk/ # Templated contracts & abstractions
└─ frontend/ # Angular workspace
tests/ # Mirrors src structure 1to1
```
There are no folders named “Module” and no nested solutions.
## 3Naming & style conventions
| Element | Rule | Example |
| ------------------------------------------------------------------------------- | --------------------------------------- | ------------------------------- |
| Namespaces | Filescoped, StellaOps.<Area> | namespace StellaOps.Scanners; |
| Interfaces | I prefix, PascalCase | IScannerRunner |
| Classes / records | PascalCase | ScanRequest, TrivyRunner |
| Private fields | camelCase (no leading underscore) | redisCache, httpClient |
| Constants | SCREAMING_SNAKE_CASE | const int MAX_RETRIES = 3; |
| Async methods | End with Async | Task<ScanResult> ScanAsync() |
| File length | ≤100 lines incl. using & braces | enforced by dotnet format check |
| Using directives | Outside namespace, sorted, no wildcards | — |
Static analyzers (.editorconfig, StyleCop.Analyzers package) enforce the above.
## 4Dependencyinjection policy
Composition root exactly one per process:
```csharp
builder.Services
.AddStellaCore() // extension methods from each runtime project
.AddPluginLoader("/Plugins", cfg); // hotload signed DLLs
```
Plugins register additional services via the IoCConfigurator convention described in the Plugin SDK Guide, §5.
Never resolve services manually (provider.GetService<T>()) outside the composition root; tests may use WebApplicationFactory or ServiceProvider.New() helpers.
Scoped lifetime is default; singletons only for stateless, threadsafe helpers.
## 5Project organisation rules
Contracts vs. Runtime public DTO & interfaces live in <Area>.Contracts; implementation lives in sibling project.
Feature folders inside each runtime project group classes by usecase, e.g.
```text
├─ Scan/
│ ├─ ScanService.cs
│ └─ ScanController.cs
├─ Feed/
└─ Tls/
```
Tests mirror the structure under tests/ onetoone; no test code inside production projects.
## 6C# language features
Nullable reference types enabled.
record for immutable DTOs.
Pattern matching encouraged; avoid long switchcascades.
Span<T> & Memory<T> OK when perfcritical, but measure first.
Use await foreach over manual paginator loops.
## 7Errorhandling template
```csharp
public async Task<IActionResult> PostScan([FromBody] ScanRequest req)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
try
{
ScanResult result = await scanService.ScanAsync(req);
if (result.Quota != null)
{
Response.Headers.TryAdd("X-Stella-Quota-Remaining", result.Quota.Remaining.ToString());
Response.Headers.TryAdd("X-Stella-Reset", result.Quota.ResetUtc.ToString("o"));
}
return Ok(result);
}
}
```
RFC7807 ProblemDetails for all non200s.
Capture structured logs with Serilogs messagetemplate syntax.
## 8Async & threading
* All I/O is async; no .Result / .Wait().
* Library code: ConfigureAwait(false).
* Limit concurrency via Channel<T> or Parallel.ForEachAsync, never raw Task.Run loops.
## 9Testing rules
| Layer | Framework | Coverage gate |
| ------------------------ | ------------------------ | -------------------------- |
| Unit | xUnit + FluentAssertions | ≥80% line, ≥60% branch |
| Integration | Testcontainers | Real Redis & Trivy |
| Mutation (critical libs) | Stryker.NET | ≥60% score |
One test project per runtime/contract project; naming <Project>.Tests.
## 10Static analysis & formatting
* dotnet format must exit clean (CI gate).
* StyleCop.Analyzers + RoslynSecurityGuard run on every PR.
* CodeQL workflow runs nightly on main.
## 11Commit & PR checklist
* Conventional Commit prefix (feat:, fix:, etc.).
* dotnet format & dotnet test both green.
* Added or updated XMLdoc comments for public APIs.
* File count & length comply with 100line rule.
* If new public contract → update relevant markdown doc & JSONSchema.
## 12Common pitfalls
|Symptom| Root cause | Fix
|-------|-------------|-------------------
|InvalidOperationException: Cannot consume scoped service...| Mismatched DI lifetimes| Use scoped everywhere unless truly stateless
|Hotreload plugin crash| Static singleton caching plugin types| Store nothing static; rely on DI scopes
>100line style violation |Large handlers or utils |Split into private helpers or new class
## 13Change log
| Version | Date | Notes |
| ------- | ---------- | -------------------------------------------------------------------------------------------------- |
| v2.0 | 20250712 | Updated DI policy, 100line rule, new repo layout, camelCase fields, removed “Module” terminology. |
| 1.0 | 20250709 | Original standards. |

View File

@ -0,0 +1,198 @@
# 19 · TestSuite Overview — **StellaOps**
*(v2.0  12Jul2025)*
> **Purpose** — Describe the **multilayer automatedtest strategy** that guards StellaOps fivesecond performance promise, security posture and API stability, and show how each layer maps to CI gates and release criteria.
---
## 0TableofContents
1. Testpyramid at a glance
2. Layer definitions & tooling
3. Directory & naming conventions
4. CI workflows & failure policy
5. Quality gates & coverage budgets
6. Evidence retention & auditability
7. Local developer quickstart
8. Flakytest triage & escalation
9. Change log
---
## 1Testpyramid at a glance
| Layer | Framework(s) | Scope | CI frequency |
| ---------------------- | ------------------------ | --------------------------------------- | ------------ |
| **Unit** | xUnit + FluentAssertions | Pure C# methods, guard clauses, mapping | Every PR |
| **Mutation** | **Stryker.NET** | Critical algorithm branches | Nightly |
| **Static analysis** | **CodeQL**, **Semgrep** | OWASP, injection, secrets | Every PR |
| **Integration** | Testcontainers + xUnit | Redis, Trivy exec, plugin hotload | Every PR |
| **Quota / throttle** | Testcontainers + Clockmock | 333scan counter, 5s &60s retryafter headers | Every PR |
| **EndtoEnd (UI)** | **Playwright C#** | Login, scan list, mute flow | Merge→main |
| **Performance** | Hyperfine + K6 | P95 latency, 40rps throughput | Nightly |
| **Security DAST** | OWASP ZAP baseline | TLS headers, auth, XSS | Nightly + RC |
| **Chaos / Resilience** | **Pumba** & Toxiproxy | Redis latency, container kill | Weekly |
| **Compliance smoke** | Spectral + JSONSchema | SBOM & API payloads | Every PR |
| **Token validity** | xUnit + ClockMock | Expiry warning, OUK update refresh, `/token/offline` flow | Every PR |
---
## 2Layer definitions & tooling
### 2.1Unit
* Target ≥80% **lineand**60% **branch** coverage (`coverlet` + ReportGenerator).
* Naming: `Method_ShouldExpected_WhenCondition`.
### 2.2Mutation
* **Stryker.NET** runs only on projects tagged `criticallogic=true` in `Directory.Build.props`.
* Threshold: ≥60% mutation score; red build <55%.
### 2.3Integration
* `RedisTestcontainer`, `TrivyServerTestcontainer`, `TestcontainersNetwork` for realistic wiring.
* Each test cleans keys and volumes; parallelisable.
* **Quota & throttle tests (new)** spin up Redis container, fix system clock to just before UTC midnight, hammer `/scan` with a stub token to validate:
1. Counter hits **200** header `XStellaQuotaRemaining: 133`; banner socket event emitted. Delay of 5 secs is added.
2. Counter hits **333** Delay of 60 secs is added.
3. At UTC midnight rollover key expires counter resets to0.
###2.4Quota / throttle layer (explicit)
* Uses the same fixture but runs in isolation to keep CI time predictable.
* Fails the pipeline if **any** of the four behaviours above misfires.
### 2.4EndtoEnd
* API suite asserts presence of `XStellaQuotaRemaining` on every successful `/scan`.
* API suite uses **async httpx** for accurate latency numbers.
* UI suite uses **Playwright** headless Chromium; Lighthouse a11y snapshot recorded.
### 2.5Performance
* Hyperfine measures CLI workflows (`SBOM_LOCAL`, `SBOM_REMOTE`, `IMAGE_WARM`).
* **K6** hits `/scan` at 40rps for 3min; checks P95 5s and errorrate=0.
### 2.6Security (DAST + SAST)
* **PHASEQUOTA_WAIT** benchmark:
* 5s median for first 30 blocked requests (soft backoff).
* Exactly 60s wall for hard waitwall.
* SAST: **CodeQL** (GitHub native) + **Semgrep OSS** ruleset.
* DAST: **ZAPbaseline** spider + passive rules; fails on High risk alerts.
### 2.7Chaos / Resilience
* **Pumba** randomly kills Trivy sidecar; test asserts queue retry.
* **Toxiproxy** injects 150ms latency on Redis; perf budget still 6s.
---
## 3Repository layout
```text
tests/
├─ unit/ # *.Unit.csproj
├─ mutation/stryker.conf.json
├─ integration/ # *.Integration.csproj
│ └─ fixtures/
├─ e2e/
│ ├─ api/pytest/ # test_*.py
│ └─ ui/playwright/ # *.spec.ts
├─ perf/
│ ├─ compose-perf.yml
│ ├─ hyperfine/
│ └─ k6/
├─ security/
│ ├─ zap-baseline.conf
│ └─ semgrep/
└─ chaos/
├─ toxiproxy/
└─ pumba/
```
Tests mirror the module namespaces; each src project owns a matching test project.
## 4CI workflows
| File | Trigger | Stages |
| ------------ | ----------------------------------------------------- | -------------------------------------- |
| ci.yml | Push / PR Lint Unit Static analysis Integration |
| e2e.yml | Mergemain | Compose stack API+UI Playwright |
| perf.yml | Nightly | Hyperfine + K6; update Grafana JSON |
| security.yml | Nightly | ZAP baseline, Trivy FS, CodeQL |
| mutation.yml | Nightly | Stryker.NET; comment PR if <threshold |
| chaos.yml | Weekly (cron) | Toxiproxy + Pumba scenarios |
| release.yml | Tag | Run all above + evidence bundling |
Failure policy: any Red gate blocks merge; nightly failures ping #stella-ci.
## 5Quality gates & budgets
| Metric | Threshold | Source | Maps to KPI |
| ----------------------------------- | ---------- | --------------------------------- | --------------- |
| Line coverage | 80% | Unit, Integration Maintainability |
| Mutation score | 60% | Stryker Defect escape |
| P95 SBOMfirst | 5s | Hyperfine | Product promise |
| P95 QUOTA_WAIT (soft) | 10s | Hyperfine + Clockmock | Predictable throttling |
| Hard waitwall accuracy | 60±1s | Hyperfine | Compliance with spec |
| P95 imageunpack | 10s | Hyperfine | SRS FRIMG1 |
| /scan errorrate | 0 | K6 | Reliability |
| ZAP High alerts | 0 | ZAP JSON | Security NFR |
| Trivy Critical CVEs in release SBOM | 0 Trivy FS | NFRSEC1 |
| Offline token expiry warning leadtime | 7days | Token tests |
Coverage & perf budgets live in tests/budgets/*.json; CI actions fail on regression.
## 6Evidence retention
| Artefact | Retention | Storage |
| ------------------ | -------------- | --------------------- |
| Hyperfine & K6 CSV | 18months | GitHub artefacts S3 |
| Mutation reports | 6months | S3 |
| ZAP & Trivy SARIF | 18months | GitHub Security tab |
| Playwright videos | Last 50 builds | MinIO |
Test logs (JUnit/Allure) 12months S3, lifecycle policy
## 7Developer quickstart
# Bring up full stack for e2e on a laptop
```bash
docker compose -f tests/e2e/compose-core.yml up -d
```
# Run unit + integration
```bash
dotnet test --collect:"XPlat Code Coverage"
# API e2e
cd tests/e2e/api
pytest -q
# UI e2e
cd tests/e2e/ui
npx playwright install
npm test
```
## 8Flakytest triage & escalation
Label failing test with flaky and open GitHub Discussion.
After 3 consecutive nightly failures, autopage <ops@stella-ops.org>.
Rootcause within next sprint or quarantine behind feature flag (max 2weeks).
*Tokenexpiry tests cannot be quarantined* — they guard offline operability.
## 9Change log
| Version | Date | Notes |
| ------- | ---------- | -------------------------------------------------------------------------------------------------------------------------- |
| v2.0 | 20250712 | Full overhaul: mutation tests, CodeQL/Semgrep, chaos layer, rolebased escalation, perf/security budgets aligned with SRS. |
| v1.0 | 20250709 | Original minimal overview |
(End of TestSuite Overview v2.0)

196
docs/21_INSTALL_GUIDE.md Normal file
View File

@ -0,0 +1,196 @@
# 21 · Installation & QuickStart Guide — **StellaOps**
*(v2.0  12Jul2025 · supersedes v1.1)*
> **Scope** — Docker Compose or Kubernetes deployment of the **opensource Core** (backend+Redis).
> For Plus/Pro features (CryptoPro TLS, LDAP, OUK, Enforcement) see the private *Commercial Install Guide*.
---
## 0Contents
0. Audience & prerequisites
1. Decide: Compose or Kubernetes
2. Quick start (Compose, 5min)
3. Verifying image signatures & SBOMs
4. First login & API token creation
5. Optional TLS & reverseproxy patterns
6. Kubernetes helmchart primer (beta)
7. Configuration cheatsheet
8. Upgrades & rollbacks
9. Uninstall / data wipe
10. Troubleshooting matrix
11. Getting help
12. Change log
---
## 0Audience & prerequisites
| Item | Minimum | Notes |
| --------- | ---------------------- | ------------------------------ |
| OS | Ubuntu22.04 or Alma9 | x8664 or ARM64 |
| CPU / RAM | 2vCPU / 2GB | Devlaptop baseline |
| Disk | 10GB SSD | SBOM + Trivy cache |
| Docker | Engine24+Composev2 | `dockerv` |
| Network | HTTPS 443 open | Optional LetsEncrypt HTTP01 |
---
## 1Choose your path
| Path | When to pick | Doc section |
| --------------------- | ----------------------------- | ----------- |
| **DockerCompose** | Single VM, PoC, laptop | §2 |
| **Kubernetes (Helm)** | Existing K3s, RKE2, EKS, etc. | §6 |
---
## 2Quick start — DockerCompose (5minutes)
### 2.1Download stack files (signed)
```bash
curl -LO https://get.stellaops.org/compose-core.yml
curl -LO https://get.stellaops.org/compose-core.yml.sig
cosign verify-blob \
--key https://stella-ops.org/keys/cosign.pub \
--signature compose-core.yml.sig \
compose-core.yml
```
### 2.2Set secrets
```bash
export REDIS_PASS=$(openssl rand -base64 18)
echo "REDIS_PASS=$REDIS_PASS" >> .env
```
### 2.3Launch
```bash
docker compose --env-file .env -f compose-core.yml pull
docker compose --env-file .env -f compose-core.yml up -d
```
Containers pulled started in <60s:
ghcr.io/stellaops/backend:1.5.0
redis:7.2-alpine
### 2.4Health check
```bash
curl -k https://localhost:8080/health
# → {"redis":"OK","version":"1.5.0","commit":"5a1b7d3"}
```
Open <https://localhost:8080> (accept selfsigned cert).
## 3Verify image provenance (optional but recommended)
```bash
BACKEND_DIGEST=sha256:… # copy from `docker images --digests`
cosign verify \
ghcr.io/stellaops/backend@$BACKEND_DIGEST \
--key https://stella-ops.org/keys/cosign.pub
```
SBOM lives in /usr/share/stellaops/sbom.spdx.json inside the image; auditors may ingest it directly.
##4Understanding the Freetier quota (333scans /UTC day)
StellaOps Core is completely free to use, but to prevent abuse the backend enforces a **daily quota of 333 scans per API token**.
* At **200 scans** a **yellow dashboard banner** reminds you of the limit (≈60% usage).
* Between **333363** scans the `/scan` endpoint responds **`429 Too Many Requests`** and a **`RetryAfter: 5`** header.
* Beyond **363** calls the server imposes a **hard 60s waitwall** (`RetryAfter: 60`).
* The counter resets at **UTC midnight**; no manual action required.
* Upgrade paths:
* **Selfhosted Core** with a second API token (still 333/day).
* **Plus / Pro licences** (unlimited).
If you script CI pipelines, honour the `RetryAfter` header to avoid unnecessary retries.
## 6First login & API token
1. Sign in with default **admin / changeme**.
2. Navigate **SettingsAPI TokensGenerate**.
3. **Each token inherits the 333scan quota** by default.
4. Store the token securely in your CI secret vault.
## 7TLS & reverseproxy options
| Scenario | Command / config |
| ------------ | --------------------------------------------------------------------------------------------------- |
| Dev l | aptop Keep backend selfsigned. |
| Public | VM + LetsEncrypt Install Caddy: caddy reverseproxy --from stella.example.com --to localhost:8080 |
| Corporate CA | Replace /certs/cert.pem + /certs/key.pem, set TLSPROVIDER=None. |
## 8Kubernetes (Helm chart β)
```bash
helm repo add stella https://charts.stella-ops.org
helm install stella-core stella/core \
--set redis.password=$REDIS_PASS \
--set ingress.host=stella.example.com
```
Resources: 300m CPU / 512Mi backend, 128Mi Redis.
NetworkPolicy, PodSecurity & CosignVerify admission controller included.
## 9Configuration cheatsheet (appsettings.json or envvars)
| Key | Default | Example |
| ------------------- | ----------- | ---------------------------- |
| ScannerPool.Workers | 1 | SCANNERPOOL__WORKERS=4 |
| FeedMerger.Cron | 0 1 ** * | FEEDMERGER__CRON="30 2 ** *" |
| Redis.Password | — | REDIS__PASSWORD=$REDIS_PASS |
| TlsProvider | OpenSslGost | TLSPROVIDER=None |
## 10Upgrades & rollbacks
```bash
# pin digest before prod rollout
docker pull ghcr.io/stellaops/backend@sha256:<NEW_DIGEST>
docker compose up -d
# rollback
docker compose up -d backend@sha256:<OLD_DIGEST>
```
Minor (Y) upgrades are nonbreaking; major (X) may change CLI flags.
## 11 Uninstall
```bash
docker compose -f compose-core.yml down -v
docker volume prune -f
rm compose-core.yml .env
```
## 12Troubleshooting matrix
| Symptom | Likely cause | Remedy |
| -------------------- | -------------------------------------- | ----------------------------------------------------------------- |
| 502 via Nginx | Backend selfsigned cert not trusted | proxy_ssl_verify off; or use proper CA |
| Feed age banner red | Cron blocked by systemd timezone skew | Run: docker exec backend dotnet Stella.Backend feed merge |
| Scan >10s | Cold Trivy DB | Second run should drop to <5s; else raise CPU / use remote mode |
## 13Getting help
* Install issues: ops@stellaops.org (rolebased mailbox).
* Security reports: security@stellaops.org (GPG key available).
* Community chat: Telegram @stellaopsxw.
* Docs index: <https://stellaops.org/docs>.
## 14Change log
| Version | Date | Notes |
| ------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| v2.0 | 20250712 | Signed download, Cosign verification, secrets primer, TLS patterns, Helm chart, rollback steps, rolebased support addresses. |
| v1.1 | 20250711 | Original Core guide. |
(End of Install Guide v2.0 — Core Edition)

View File

@ -0,0 +1,245 @@
#22 · PipelineRecipes Library— **StellaOps**
#Recipes & Pipeline Library
> *Readytocopy snippets for CI/CD engines, local shells, and K8s jobs.*
> Each recipe honours the **sub5s pledge**: SBOMfirst when possible, ΔSBOM when layers are cached, and imageunpack only as a fallback.
---
##0RegistryPrimer
All agent images and helper tools are published to a **private, anonymous registry**
```
registry.git.stella-ops.ru
```
* **Pulls are readonly & unauthenticated.**
* Cosign signatures are embedded (`*.sig`) and verified at runtime when the host has `cosign` installed.
* To mirror for airgapped OUK installs, export with:
```
oras pull registry.git.stella-ops.ru/library/santech:1.0 --output ./ouk-bundle
```
---
##1Shell Quickstarts
###1.1Scan a Local Image (SBOMfirst)
```bash
# Free tier: 333 scans/day without large delay added
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
https://stella.local/registry/santech:1.0 \
scan \
--image python:3.12-slim \
--endpoint https://stella.local \
--sbom-type spdx-json \
--threshold High
```
* `--sbom-type` enumerates **`trivy-json-v2 | spdx-json | cyclonedx-json`**; defaults to autodetect when omitted.
* Exitcode maps to policy (nonzero if blocked).
###1.2Delta SBOM Path
```bash
# Free tier: 333 scans/day without large delay added
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-e STELLA_DELTA=1 \
https://stella.local/registry/santech:1.0 \
scan \
--image myapp:latest \
--delta \
--endpoint https://stella.local
```
`--delta` triggers the `/layers/missing` fast check; observed P951s on cached bases.
---
##2GitHub Actions
```yaml
# .github/workflows/stella-scan.yml
name: Stella Scan
on:
push:
branches: [main]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t ${{ github.repository }}:${{ github.sha }} .
- name: Scan with StellaOps (ΔSBOM + YAML policies)
run: |
# Free tier: 333 scans/day without large delay added
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $GITHUB_WORKSPACE/policy:/policy:ro \
https://stella.local/registry/santech:1.0 \
scan \
--image ${{ github.repository }}:${{ github.sha }} \
--delta \
--policy-file /policy/scan-policy.yaml \
--endpoint ${{ secrets.STELLA_API }}
```
*Stores SARIF to `$RUNNER_TEMP/stella-report.sarif`; upload via `actions/upload-artifact` if desired.*
---
##3GitLab CI
```yaml
stella_scan:
image:
name: ttps://stella.local/registry/santech:1.0
entrypoint: [""]
stage: test
variables:
STELLA_ENDPOINT: "https://stella.local/api"
script:
- docker build -t myapp:$CI_COMMIT_SHORT_SHA .
- >
# Free tier: 333 scans/day without large delay added
./santech scan
--image myapp:$CI_COMMIT_SHORT_SHA
--sbom-type cyclonedx-json
--threshold Critical
--endpoint $STELLA_ENDPOINT
allow_failure: false
artifacts:
when: always
paths:
- stella-report.html
```
*`allow_failure:false` enforces gate by failing the stage on Critical findings.*
---
##4Tekton Pipelines (K8s)
```yaml
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: stella-scan
spec:
workspaces:
- name: dockerconfig
steps:
- name: scan
image: https://stella.local/registry/santech:1.0
script: |
#!/usr/bin/env sh
# Free tier: 333 scans/day without large delay added
santech scan \
--image $(params.image) \
--delta \
--threshold High \
--endpoint $(params.endpoint)
```
---
##5Policy Import / Export
###5.1Import YAML Policy via CLI
```bash
curl -X POST https://stella.local/api/v1/policy/import \
-H "Authorization: Bearer $TOKEN" \
-F "file=@scan-policy.yaml"
```
###5.2Export & Commit to Git
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
https://stella.local/api/v1/policy/export \
> policy-backup-$(date +%F).yaml
git add policy-backup-*.yaml && git commit -m "Policy snapshot"
```
---
##6Offline OUK Example
Inside an **airgapped** cluster:
1. Run `ouk-fetch.sh` from the admin node.
2. Load images into the internal registry:
```bash
ctr -n k8s.io images import ./ouk-bundle/*.tar
```
3. Use the same pipeline snippets; DNS points to `registry.git.stella-ops.ru` via local CoreDNS override.
---
##7VariantD Enforce Gate in Prod
```bash
# Free tier: 333 scans/day without large delay added
santech scan \
--image registry.prod.corp/app:${TAG} \
--delta \
--policy-file prod.rego \
--enforce \
--endpoint https://stella.prod \
|| { echo "Security gate blocked release!"; exit 1; }
```
*`--enforce` turns warnings into nonzero exit codes.*
---
##8CheatSheet (CLI Flags)
| Flag / Env | Meaning | Default |
|----------------------------|---------------------------------------------------|---------|
| `--sbom-type` | Force SBOM output format (`trivyjson-v2` …) | *Auto* |
| `--delta` `STELLA_DELTA=1` | Enable layer diff / `/layers/missing` fast path. | Off |
| `--policy-file` | Import YAML/Rego before scan. | None |
| `--threshold` | Fails scan if sev ≥ threshold. | High |
| `--enforce` | Exit nonzero on policy block. | Off |
| `--endpoint` | API base URL. | `http://localhost:8080` |
| `--insecure` | Skip TLS verify (test only!). | Off |
---
##9FAQ
**Q  I need Syft+SPDX, what changes?**
A Set `--sbom-type spdx-json`; Trivy is bypassed and the scanner plugin selects Syft.
**Q  Can I run Santech as rootless?**
A Yes; mount the hosts Docker socket via `--userns=keep-id` or use `--context host` with nerdctl.
**Q  Does ΔSBOM work for multiarch manifests?**
A Today it only checks `linux/amd64` layers; roadmap item *Q12026* widens support.
---
##10Change Log
| Date | Note |
|------------|--------------------------------------------------------------|
| 20250714 | Added internal registry, ΔSBOM, multiformat & policy flows |
| 20250712 | Initial public recipe set (GitHub, GitLab, Tekton, shell). |
---

147
docs/23_FAQ_MATRIX.md Normal file
View File

@ -0,0 +1,147 @@
#23 · FAQ Matrix —StellaOps 
#FAQ & Support Matrix
A living list of the questions we get every day, plus a compact matrix of what is **SupportedNow**, **InPreview**, and **OntheRoadmap (TODO)**.
---
##0QuickLegend
| Mark | Meaning |
|------|---------|
| ✅ | Fully supported in the current release |
| 🅿️ | Preview / optin behind a feature flag |
| 🛠 | Planned in the **6month** roadmap (Feature Matrix “TODO”) |
| 🚧 | Longerterm; 912month horizon or beyond |
---
##1General
| # | Question | ShortAnswer | Status |
|---|----------|-------------|--------|
| G1 | *Why launch **another** DevSecOps product?* | Existing scanners are either SaaSonly, slow, or lack offline & Russian language feeds. StellaOps focuses on *speed(<5s), modularity, airgap friendliness*, and an AGPL codebase that enterprises can extend inhouse. | ✅ |
| G2 | *What tech stack?* | Backend **.NET 9** + Redis; runners are OCI images (Trivy, Syft, Grype). UI Angular17. | ✅ |
| G3 | *License?* | **AGPLv3** for all core repos; plugins inherit if linked. | ✅ |
| G4 | *Where do I report bugs?* | Open an issue in `git.stella-ops.ru/stella/core` or ping `#stella-ops` on Matrix. | ✅ |
---
##2Installation & Upgrades
| # | Question | Answer | Status |
|---|----------|--------|--------|
| I1 | *How do I pull agent images now?* | All official images are in the **anonymous readonly registry** `registry.git.stella-ops.ru`. No auth token required for pull. | ✅ *(new)* |
| I2 | *Can I still use GHCR?* | Images remain mirrored for convenience but are not signed; internal registry is the source of truth. | ✅ |
| I3 | *How to upgrade from ≤v0.8?* | Regenerate `dockercompose.yml` with the bootstrap script; volumes remain intact. Import legacy muterules via `/policy/import`. | ✅ |
| I4 | *Helm charts?* | K8s Helm chart is under `deploy/helm`; undefaulted (requires `values.yaml`). | 🅿️ |
---
##3SBOM & Scanning
| # | Question | ShortAnswer | Status |
|---|----------|-------------|--------|
|---|----------|-------------|--------|
| **S1** | *Why exactly **333 scans**?* | Covers p95 workload of SMBs (~290 builds/day) while keeping infra costs <$5/mo per user and nudging larger orgs toward Plus/Pro. | ✅ |
| **S2** | *How is the limit technically enforced?* | Each `/scan` request carries a **ClientJWT**. The Quota plugin atomically increments `quota:<token>:<date>` in Redis. Soft (5s) and hard (60s) waitwalls ensure fair use. | ✅ |
| **S3** | *What if my site is fully offline?* | Every **OUK tarball** contains a fresh ClientJWT valid **30days**. Uploading the OUK refreshes the token automatically; no Internet required. | ✅ |
| S4 | *Can I pool multiple tokens?* | Yes, but each token has its own 333/day budget. Use distinct tokens per CI line if you need more throughput. | ✅ |
| S5 | *Does quota enforcement affect performance?* | No. Legitimate scans still complete in <5s; blocked scans incur only their specified waitwall. | |
| S6 | *Which SBOM formats does Stella emit?* | Builtin: **`trivy-json-v2`**, **`spdx-json`**, **`cyclonedx-json`**. | |
| S7 | *What is ΔSBOM and how fast is it?* | Uploads only new layers; P951s on cached bases. | |
| S8 | *Windows container scanning?* | Runner binaries compile on Windows, but layerunpack path is unoptimised; full support 🚧. | 🚧 |
---
##4PolicyasCode
| # | Question | Answer | Status |
|---|----------|--------|--------|
| P1 | *How are mutes & blocks stored now?* | Default: **YAML** (`scan-policy.yaml`) in Mongo (versioned). Import / export via `/policy/{import,export}` or Settings Policies. | |
| P2 | *Why YAML over OPA?* | YAML lowers entry barrier; advanced users may embed **Rego** snippets. Firstclass Rego evaluation is 🛠. | 🛠 |
| P3 | *CLI enforcement?* | Pass `--policy-file path` plus `--enforce` to fail builds on violations. Exitcode reflects policy gate. | |
| P4 | *Audit history?* | Every policy change writes an immutable record (`audit_policies` collection) and appears in UI *History* tab. | |
---
##5Registry & Offline Use
| # | Question | Answer | Status |
|---|----------|--------|--------|
| R1 | *Is the internal registry mandatory?* | No, but recommended for sovereignty & signature verification (`cosign verify`). | |
| R2 | *How to mirror for OUK?* | `oras pull registry.git.stella-ops.ru/library/* --output ./ouk-bundle` import on the target via `ctr images import`. | |
| R3 | *Does the backend fetch external feeds?* | Only when `--feeds.auto=1`; OUK installs run fully offline with NVD packed in the tarball. | |
---
##6Performance
| # | Scenario | Target | Achieved (July2025) |
|---|----------|--------|----------------------|
| Local SBOM scan (`alpine`) | **5s** | 4.2s P95 |
| ΔSBOM warm base | **1s** | 0.8s P95 |
| Image unpack (200MB) | **10s** | 8.6s P95 |
*Numbers measured on 4vCPU / 8GB Ubuntu22.04 runner.*
---
##7Security & Compliance
| # | Question | Answer | Status |
|---|----------|--------|--------|
| C1 | *How are images signed?* | Cosign signatures pushed alongside each tag (`*.sig`). Santech verifies on pull. | |
| C2 | *Supplychain attestation (SLSA)?* | SLSAgen at build time and verification in runner is 🛠 (≤6months). | 🛠 |
| C3 | *Rekor transparency log?* | Local Rekor mirror for offline installs is 🚧 (912months). | 🚧 |
| C4 | *TLS ciphers?* | Default OpenSSL suites; plugin allows GOST/SM (via `ITlsProvider`). | |
---
##8Roadmap / FutureFeatures
| Area | Feature | ETA | Notes |
|------|---------|-----|-------|
| UI | Modular route plugins | Q12026 | Dynamic Angular module loader |
| SBOM | Multiarch ΔSBOM | Q12026 | Layer digest per arch |
| Policy | Rego native engine | Q12026 | `opa eval` inproc |
| Supply chain | SLSA provenance | Q12026 | Level 3 target |
| Integrity | Rekor mirror | Q22026 | Airgap friendly |
| Ecosystem | Community plugin market | Q22026 | Curated index in UI |
| Scale | Redis Cluster autoshard | Q32026 | Transparent failover |
---
##9Troubleshooting
| Symptom | Likely Cause | Fix |
|---------|--------------|-----|
| **`ER_BAD_SV` error on scan** | SBOM format flag mismatch | Set correct `--sbom-type` or let autodetect. |
| ΔSBOM still uploads full SBOM | Cache cold or digest mismatch | Check `docker history` shows reused layers; bump builder version. |
| Policy file invalid | YAML schema error | Run `/policy/validate` endpoint; lint with VS Code schema. |
| Pull fails with 401 | Corporate proxy intercepts registry | Mirror to onpremise Harbor; set `--registry` flag. |
---
##10Licensing & Community
| # | Question | Answer |
|---|----------|--------|
| L1 | *Can I build a commercial fork?* | AGPL allows commercial services but derivatives must remain AGPL if distributed. |
| L2 | *Commercial support?* | Community only today; paid support partners in discussion. |
| L3 | *How to contribute a plugin?* | Fork implement DI contract (`IScannerRunner`, etc.) PR + ADR. |
---
##11Change Log
| Date | Highlights |
|------------|------------|
| 20250714 | Added internal registry, multiformat SBOM, ΔSBOM, PolicyasCode, updated roadmap (SLSA/Rekor) |
| 20250630 | Initial public FAQ matrix |
---
*(End of FAQ Matrix v2.0)*

196
docs/24_OUK_ADMIN_GUIDE.md Normal file
View File

@ -0,0 +1,196 @@
#24 · Offline UpdateKit (OUK) Administration Guide —StellaOps
# OfflineUpdateKit(OUK) — AdministratorGuide
StellaOps is designed for *offlinefirst* deployments—e.g. regulated datacentres that cant call out to GitHub or NVD.
The **OfflineUpdateKit (OUK)** bundles *all* mutable security data (Trivy DB, NVD JSON, OSV feeds, UI locales, policy bundles) into a signed, singlefile archive so you can refresh an isolated cluster in minutes.
---
## 0Quick Start (TL;DR)
1. **Download** the latest release asset
```bash
curl -L -O https://git.stella-ops.ru/stella/ops/-/releases/latest/download/ouk-20250715.tar.gz
curl -L -O https://git.stella-ops.ru/stella/ops/-/releases/latest/download/ouk-20250715.tar.gz.sig
```
2. **Verify** the Cosign signature
```bash
cosign verify-blob \
--key cosign.pub \
--signature ouk-20250715.tar.gz.sig \
ouk-20250715.tar.gz
```
3. **JWT Refresh** the tarball already contains a **`client.jwt`** file valid
for **30 days**. Importing the OUK automatically refreshes the token used for
quota accounting in offline mode.
4. **UI →Settings →OfflineUpdates →Upload** the tarball
5. Wait for the toast: **“Feeds updated · TrivyDB20250714 · NVD20250715”**
6. Sub5s scans now use the fresh data—done!
---
## 1What the OUK Contains
| Component | Typical size |
|----------------------------------------|--------------|
| Trivy vulnerability DB (`vuln.db.tgz`) | 50MB |
| NVD JSON feed (all years) | 1.4GB |
| OSV JSON snapshots | 500MB |
| BDU XML feed (🇷🇺 optional) | 25MB |
| UI locale bundles | 100kB |
| Policy pack snapshots | 5kB |
| SBOM of the kit (`manifest.spdx.json`) | <1MB |
| Cosign signature (`*.sig`) | 1kB |
| ClientJWT (`client.jwt`) 30d | 2kB |
All files are **contentaddressed**; the SBOM references each SHA256 so auditors can trace provenance.
---
## 2Security Model
* Every tarball is signed with the **StellaOps project key** (`cosign.pub`, fingerprint `AB1234CD56EF…`).
* The backend refuses any archive that fails signature or digest checks.
* The embedded SBOM gives auditors full transparency into exact feed versions.
---
## 3Obtaining anOUK Tarball
### 3.1OfficialBuilds
* Produced automatically on the **first Friday of every month** by the Release pipeline (see *13_RELEASE_ENGINEERING_PLAYBOOK.md*).
* Stored under **GitLab →Releases →Assets** with the filename pattern `ouk-YYYYMMDD.tar.gz`.
* Example download via curl:
```bash
curl -L -O https://git.stella-ops.ru/stella/ops/-/releases/v2.3/downloads/ouk-20250715.tar.gz
curl -L -O https://git.stella-ops.ru/stella/ops/-/releases/v2.3/downloads/ouk-20250715.tar.gz.sig
```
---
## 4Uploading ViaUI
1. Log in as **system:admin** (role = Owner).
2. Navigate **Settings →OfflineUpdates**.
3. Press **Upload** and pick `ouk-YYYYMMDD.tar.gz`.
4. Backend workflow
1. Streams the file to `/tmp`.
2. Runs `cosign verify-blob`.
3. Extracts content into `var/lib/stella/db/` (readonly).
4. Flushes Redis cache.
> After Cosign verification the backend copies **`client.jwt`** into
> `/var/lib/stella/tokens/`, replacing the previous offline token and resetting
> its expiry countdown.
5. Audit log emits `ouk_update` event (actor, datetime, SHA256).
The UI toast now shows
> “Feeds updated + OfflineToken refreshed (validtill *17Aug 2025*)”.
---
## 5UploadingViaCLI (REST API)
No custom tool is required`curl` does the job.
```bash
ACCESS_TOKEN=$(cat token.txt) # bearer from OpenIddict
SIGNATURE_B64=$(base64 -w0 ouk-20250715.tar.gz.sig)
curl -X POST https://stella.local/api/admin/ouk \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "X-Signature: ${SIGNATURE_B64}" \
--data-binary @ouk-20250715.tar.gz
```
*HTTP 201* is success; any non2xx code means verification or extraction failed.
---
## 6Automated Sync (Advanced)
When outbound Internet is *allowed* but inbound isnt:
1. Deploy **StellaOps.Registry** (internal Docker registry) on a host that can reach the Internet.
2. Cron job pulls the latest OUK with curl, verifies it, stores in an internal S3 bucket.
3. Offline cluster fetches nightly via **MinIO Gateway** URL:
```bash
curl -L -O https://minio.infra/offline/ouk-latest.tar.gz
curl -L -O https://minio.infra/offline/ouk-latest.tar.gz.sig
# repeat verification and upload steps
```
Remember to propagate **`client.jwt`** together with the feeds if you split the
tarball into multiple artefacts; otherwise the offline cluster may hit
*“Token expired”* errors.
---
## 7RollbackStrategy
* The backend keeps the **last three OUK snapshots** in `/var/lib/stella/ouk-backup/`.
* UI OfflineUpdates **Restore** next to the desired date.
* Operation swaps a symlink—≈3s, no service restart.
---
## 8Troubleshooting
| Symptom | Likely cause | Fix |
|--------------------------------------|----------------------|----------------------------------------------|
| Signature verification failed | Wrong key/tampered | Redownload; compare key fingerprints |
| Feed age tile stays red | Redis cache stale | `stella admin redis flushall` |
| `io/fs: readonly file system` | Volume mounted RO | Remount RW or move OUK path |
| NVD import >10min | Slow disk | Move DB path to SSD/LVM cache |
| *“Token expired”* banner after OUK upload | `client.jwt` missing or outofdate | Rerun `ouk fetch`; ensure tarball contains fresh token. |
---
## 9NonCommercialUsage Rules (English)
1. **Free for internal assessments** by individuals or organisations.
2. **SaaS resale/rehosting is forbidden** without explicit written consent (AGPL§13).
3. If you redistribute a modified build you **must** publish complete source under the same licence and keep StellaOps attribution.
4. Thirdparty dependencies remain under their respective licences (MIT, Apache2.0, ISC, BSD).
5. Deployments in stateregulated or classified environments must follow **ФЗ187** and exportcontrol law.
---
## 10BestPracticesSnapshot
* Rotate OUK uploads at least **monthly**; weekly during high CVE activity.
* Store OUK assets in an **immutable bucket** (S3 WORM, Artifactoryrelease) to thwart tampering.
* Keep `cosign.pub` on an offline rootoftrust USB; rotate keys yearly.
* During audits, export `manifest.spdx.json` as evidence of feed provenance.
---
## 11FAQ
**Q:** *Does OUK update Docker base images?*
**A:** No—OUK is for **data** only. Refresh base images via internal registry or CI pipelines.
**Q:** *Why not bundle Grype DB too?*
**A:** Grype consumes Trivy DB format directly; duplication saved 500MB.
---
## 12Change Log
| Version | Date | Notes |
|---------|------|-------|
| v1.2 | 20250716 | Switched CLI to curl, removed DIY build, dropped “Location” column, clarified download example. |
| v1.1 | 20250715 | Added UI upload flow, MinIO sync, usage rules, Cosign commands. |
| v1.0 | 20250709 | Initial OUK admin guide |
*(End of Offline UpdateKit Administration Guide v1.0)*

129
docs/29_LEGAL_FAQ_QUOTA.md Normal file
View File

@ -0,0 +1,129 @@
#29 · Legal FAQ — *333 Scan/Day FreeTier Quota*
> **Status:** Published applies to all opensource (AGPL3.0orlater) builds
> **Audience:** project contributors, downstream packagers, OSS users, lawyers
> **Scope:** clarifies how the dailyquota mechanism interacts with copyright
> licences, fairuse, forking rights and the AGPLnetworkuse clause.
> **Not a contract:** this FAQ is **informational** and does not replace the
> actual licence text (GNU AGPL3.0) shipped with every source tarball.
---
## 1·Is the *333 scans per UTC day* limit a **licence** restriction?
**No.**
The quota is an **operational usage limit** enforced by runtime code
(`StellaOps.Quota` plugin).
The source code itself remains available under the
[GNU  Affero General Public Licencev3](https://www.gnu.org/licenses/agpl3.0.html).
Any person may:
1. **Receive the complete corresponding source** (AGPL §6).
2. **Modify** or **remove** the quota logic, and
3. **Run** those modified versions, **even over a network** (AGPL §13),
provided they also comply with the licence duties (e.g. **offer**
their own modified source to users who interact with the modified
program over a network).
---
## 2·Why doesnt the quota violate AGPL §0 or §13?
* **§0** (Freedom to run the program)  You can run unmodified or
modified copies of StellaOps for any purpose and in any quantity
inside your own infrastructure. The quota only limits *how many scan
operations the official binaries will accept*; it does **not** forbid
you from creating binaries without that limit.
* **§13** (Remotenetwork interaction)  If you provide StellaOps as a
service and keep the quota, users interact with an unmodified AGPL
work, so no extra obligations arise.
If you **remove** or **change** the quota, that counts as a
modification; under §13 you must **make your modified source code
available** to those remote users.
---
## 3·May I **fork** the project and delete the quota altogether?
Yes the AGPL expressly permits this.
However, three practical notes apply:
| Aspect | Obligation / Consequence |
|--------|-------------------------|
| **Source availability** | You *must* provide the full modified source (including build scripts) under AGPLcompatible terms. |
| **Trademarks** | The name **“StellaOps”**, the starlogo and the “333” banner are trademarked. Remove them or obtain prior written consent before redistributing a modified build. |
| **Update Kit tokens** | Official OfflineUpdateKit (OUK) tarballs embed signed ClientID JWTs that expect the quota codepath. If you strip that path, you will also need to maintain your own OUK feed or rebuild the tarballs. |
---
## 4·What about **educational labs** or “Hack Week” events that need more than 333 scans?
You have three options:
1. **Request a shortlived *Workshop Token***  project maintainers can
issue a signed JWT that temporarily raises the limit (subject to
availability).
2. **Spin up additional tokens**  each API token carries its own 333
scans/day budget; e.g. a class of 40 students can share ten tokens.
3. **Compile without quota**  ideal for private, nonpublic courses;
remember to keep the modified source accessible to participants.
None of these options require a commercial licence fee.
---
## 5·Does the quota create a **fieldofuse restriction** (GPL FAQ concern)?
No. The limit is *quantitative* (how many scans per unit of time), not a
prohibition on *where* or *for what* purpose you run the software.
Therefore it does **not** impose an additional fieldofuse restriction
and remains compatible with AGPL§7.
---
## 6·How does the **Offline Update Kit** interact with licence tokens?
* Every OUK tarball ships a **signed ClientID JWT** with 30day
validity.
* The token is stored at `/keys/client.jwt` on the backend container and
refreshed on each OUK upload.
* If you fork the codebase and remove the quota, you must either
(a) ignore the token entirely, or
(b) generate and sign your own tokens the signing key is **not**
included in the public repo for security reasons.
---
## 7·Can forks override the limit?
Yes. Forks could provide ClientID tokens with `maxScansPerDay = 0`
(unlimited). The same runtime code path is used; no proprietary
mechanism exists inside the opensource core.
---
## 8·Why 333 not 100, 500 or 1000?
* 333 ≈ the 95thpercentile daily build count of **small/mediumsized
selfhosted GitLab instances** we surveyed in 2024.
* It leaves *headroom* for bursty days while nudging larger teams
towards Plus/Pro the main revenue driver that funds continued
development of the opensource core.
See *02_WHY.md §5* for the full productmanagement rationale.
---
## 9·Who can I contact for **licensing support**?
* Email: **legal@stellaops.org**
---
## 10·Change Log
| Version | Date | Comment |
|---------|------|---------|
| **v1.0** | 18Jul2025 | First public revision aligns with quota rev2.0. |

View File

@ -0,0 +1,186 @@
#26 · QuotaEnforcement Flow (333 Scans/Day)
> **Audience:** backend contributors, UI devs, CIplugin authors, thirdparty
> tool builders
> **Version:** 1.0 (18 Jul 2025) — aligns with Quotarev 2.0
> **Scope:** runtime dataflow, key formats, timing budget, error model
---
## 0 Why another doc?
* **SRS** lists the *requirements* (FR4, FR4a…).
* **ModuleSpecs** describe the *code surface* (`IQuotaService`, `QuotaVerdict`).
* **This file** stitches everything together so future maintainers can reason
about race conditions, UX timing and API behaviour in one place.
---
## 1 HighLevel Sequence
```mermaid
sequenceDiagram
participant CLI as SanTech CLI
participant API as API Gateway
participant QU as Quota Service
participant RD as Redis
participant SCN as Scan Runner
CLI->>API: POST /scan
API->>QU: CheckAsync(token)
QU->>RD: LUA INCR quota:<token>
alt Remaining > 0
RD-->>QU: counter + reset ts
QU-->>API: verdict {IsAllowed=true}
API->>SCN: enqueue scan
SCN-->>API: result
API-->>CLI: 200 OK (headers: remaining, reset)
else Remaining == 0 → soft wall
RD-->>QU: counter==limit
QU-->>API: verdict {RetryAfter=5 s}
API-->>CLI: 429 Too Many (RetryAfter: 5)
else HardWall (≥limit+30)
RD-->>QU: counter≥limit+30
QU-->>API: verdict {RetryAfter=60 s}
API-->>CLI: 429 Too Many (RetryAfter: 60)
end
```
* **Soft waitwall** applies to the *first 30* blocked scans.
* **Hard waitwall** (60s) kicks in afterwards until the counter resets at
**00:00UTC**.
---
## 2 Key Redis Structures
| Key pattern | Type | TTL | Purpose |
|-------------|------|-----|---------|
| `quota:<token>:<yyyymmdd>` | *string* | *expires at UTC midnight* | Counter starting at `1` |
### Typical value progression
```json
{
"quota:abc123:2025-07-18": "198",
"quota:abc123:2025-07-18-RESET": "2025-07-19T00:00:00Z"
}
```
---
## 3 QuotaVerdict DTO
```csharp
public readonly record struct QuotaVerdict(
bool IsAllowed,
int Remaining,
DateTimeOffset ResetUtc,
TimeSpan RetryAfter)
{
public static QuotaVerdict Allowed(int remaining, DateTimeOffset resetUtc)
=> new(true, remaining, resetUtc, TimeSpan.Zero);
public static QuotaVerdict Blocked(int remaining, DateTimeOffset resetUtc,
TimeSpan retryAfter)
=> new(false, remaining, resetUtc, retryAfter);
}
```
* **IsAllowed** `false` ⇒ API must return `429`
* **RetryAfter** `> 0` ⇒ populate `RetryAfter` header (seconds)
---
## 4 HTTP Headers & Error Model
| Header | Example | Meaning |
|--------|---------|---------|
| `XStellaQuotaRemaining` | `117` | Scans left for the token today |
| `XStellaReset` | `20250718T23:59:59Z` | When the counter resets |
| `RetryAfter` | `5` or `60` | Seconds the client should backoff |
Error body (RFC 7807):
```json
{
"type": "https://stella-ops.org/probs/quota",
"title": "Daily quota exceeded",
"status": 429,
"detail": "This token exhausted its 333 scans for 20250718.",
"quota": {
"remaining": 0,
"reset": "20250718T23:59:59Z"
},
"traceId": "00-1db39d4f3ab16fa1"
}
```
---
## 5 UI Behaviour
| Threshold | Visual cue |
|-----------|------------|
| **133 scans remaining** | Yellow banner “*60% of free tier used*” |
| **0 remaining** (soft) | Red toast “*Backoff 5s*”; spinner shows countdown |
| **0 remaining** (hard) | Modal blocks *New Scan* button; shows **HH:MM:SS** until reset |
*WebSocket `quota/events` pushes live counters to avoid polling.*
---
## 6 Timing Budget
| Operation | Budget (P95) | Impl detail |
|-----------|--------------|-------------|
| RedisLUA INCR | ≤0.8ms | SHA script cached |
| Verdict calc | ≤0.2ms | branchless math |
| Header injection | ≤0.1ms | middleware sets `OnStarting` callback |
| Soft delay | **exactly 5000ms** ±50ms | `Task.Delay` in controller |
| Hard delay | **exactly 60000ms** ±100ms | same |
---
## 7 CLI AutoRetry Logic (SanTech v0.8+)
```csharp
var resp = await http.PostAsync("/scan", payload, ct);
if (resp.StatusCode == (HttpStatusCode)429 &&
autoRetry && retryCount < 1)
{
var after = int.Parse(resp.Headers.RetryAfter.Delta!.Value.TotalSeconds);
Console.Error.WriteLine($"Quota hit waiting {after}s…");
await Task.Delay(TimeSpan.FromSeconds(after), ct);
retryCount++;
goto Retry;
}
```
*Retrieves `RetryAfter`; retries **once** if ≤60s.*
---
## 8 Edge Cases & Tests
| Case | Expected behaviour | Covered by |
|------|--------------------|-----------|
| Redis restart (counter key lost) | API treats missing key as `0`; safe default | ITQUOTAREDIS |
| Two parallel scans exceed limit simultaneously | Only one passes; second gets `429` | RaceCondTests.cs |
| Token with custom limit =100 | Soft wall at100; headers reflect custom limit | QuotaCustomLimitTests.cs |
---
## 9 Migration Notes (v0.7  v0.8)
* Old key `quota:<token>` (no date) **deprecated** — migration script copies
value to the new *dated* key and sets TTL to next UTC midnight.
---
## 10 Change Log
| Ver | Date | Note |
|-----|------|------|
| 1.0 | 18Jul2025 | Initial endtoend flow doc (Quota rev2.0). |