Here’s a compact, no‑fluff onboarding brief to get a mid‑level .NET dev productive on StellaOps in a couple of days. --- # StellaOps – Mid‑Level .NET Onboarding (Quick Start) ## 0) What you’re building (context in one minute) StellaOps is a sovereign, air‑gap‑friendly SBOM→VEX platform written in .NET 10. Core ideas: deterministic scans, cryptographic attestations (DSSE/in‑toto; optional PQC), trust lattices for VEX decisions, and a replayable audit trail. --- ## 1) Repos to open first (read in this order) 1. `src/StellaOps.Scanner.WebService/` — scanning surfaces, rules plumbing, lattice calls. 2. `src/StellaOps.Vexer/` (a.k.a. Excititor) — VEX verdict engine & trust‑merge logic. 3. `src/StellaOps.Sbomer/` — SBOM ingest/normalize (CycloneDX/SPDX). 4. `src/StellaOps.Authority/` — keys, attestations, license tokens, Rekor integration. 5. `src/StellaOps.Scheduler/` — batch & replay orchestration. 6. `src/StellaOps.Shared/CanonicalModel/` — canonical entities & graph IDs (read carefully). Tip: keep `docs/modules/platform/*` open for protocol notes (DSSE envelopes, lattice terms). --- ## 2) Local dev up in ~10–15 min * Prereqs: .NET 10 SDK (preview), Docker, Node (for Angular UI if needed), WSL2 optional. * From repo root: ```bash # infra docker compose -f compose/offline-kit.yml up -d # Mongo/Postgres/Rabbit/MinIO per profile # backend dotnet restore dotnet build -c Debug # run a focused slice (scanner + authority) dotnet run --project src/StellaOps.Authority/StellaOps.Authority.csproj dotnet run --project src/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj ``` * Environment: copy `env/example.local.env` → `.env`, then set `STELLAOPS_DB` provider (`Mongo` or `Postgres`) and `AUTHORITY_*` keys. --- ## 3) Deterministic testcases to run locally (prove your env is correct) These are “golden” replays: same inputs → same graph & hashes. 1. **Hello‑SBOM → VEX Not‑Affected (Reachability‑false)** ```bash dotnet test tests/Determinism/Det_SbomToVex_NotAffected.csproj ``` Checks: identical GraphRevisionID and DSSE payload hash across two consecutive runs. 2. **In‑toto chain: source→build→image attestation** ```bash dotnet test tests/Attestations/Att_InToto_Chain.csproj ``` Checks: DSSE envelope canonicalization stable; signature over CBOR‑canonical JSON matches stored hash. 3. **Lattice merge: vendor VEX + runtime signal** ```bash dotnet test tests/Lattice/Lattice_VendorPlusRuntime.csproj ``` Checks: merge verdict is stable with the same input set order; produces identical TrustReceipt. If any golden snapshot differs, your clock/locale/line‑endings or JSON canonicalizer is misconfigured. --- ## 4) Coding conventions (cryptographic attestations & determinism) * **JSON**: serialize with our `CanonicalJson` (UTF‑8, sorted keys, no insignificant whitespace, `\n` line endings). * **DSSE**: always embed `payloadType` = `application/vnd.stellaops.trust+json`. * **Hashing**: BLAKE3 for internal content addressing, SHA‑256 where interop requires. * **Keys**: use `Authority.KeyRing` provider (Ed25519 by default; PQC Dilithium optional flag `AUTHORITY_PQC=on`). * **Timestamps**: use `Instant` (UTC) with truncation to milliseconds; never `DateTime.Now`. * **IDs**: graph nodes use `HashStableId` derived from canonical bytes; never database autoinc for public IDs. * **VEX**: verdicts must include `proofs[]` (e.g., reachability, config‑guards, runtime path) and a `receipt` signed by Authority. * **Repro**: any function that affects verdicts must be pure or behind a deterministic adapter. Snippet (sign a DSSE envelope deterministically): ```csharp var payload = CanonicalJson.Serialize(trustDoc); var env = DsseEnvelope.Create("application/vnd.stellaops.trust+json", payload); var signed = await keyRing.SignAsync(env.CanonicalizeBytes()); await rekor.SubmitAsync(signed, RekorMode.OfflineMirrorIfAirgapped); ``` --- ## 5) Minimal daily loop * Pick one starter issue (below). * Write unit tests first; ensure golden snapshots match. * Run `dotnet test --filter Category=Determinism`. * Commit with `feat(scanner|vexer|authority): …` and include the GraphRevisionID delta in the body. --- ## 6) Three starter issues (teach the canonical data model) ### A) Normalize CycloneDX components → Canonical Packages **Goal**: map CycloneDX `components` to `CanonicalPackage` with stable IDs. **Where**: `StellaOps.Sbomer` + tests in `tests/Determinism/Det_SbomMapping`. **Done when**: * Two equivalent SBOMs (field order shuffled) map to identical package set & IDs. * Snapshot `CanonicalPackageSet.hash` is stable. * Edge cases: missing `purl`, duplicate components, case differences. ### B) Implement “Not‑Affected by Configuration” proof **Goal**: add proof generator that marks a CVE as not‑affected if a config gate is off. **Where**: `StellaOps.Vexer/Proofs/ConfigSwitchProof.cs`. **Done when**: * Given `FeatureX=false`, CVE‑1234 becomes `not_affected` with proof payload including `configPath`, `observed=false`. * Deterministic proof hash and DSSE receipt exist. * Lattice merge keeps vendor “affected” but flips to `not_affected` when runtime/config proof weight > threshold. ### C) Authority offline Rekor mirror submitter **Goal**: if air‑gapped, write DSSE entries to local mirror; sync later. **Where**: `StellaOps.Authority/Rekor/RekorMirrorClient.cs`. **Done when**: * `RekorMode.OfflineMirrorIfAirgapped` stores canonical entry (JSON+hash path). * `rekor sync` job replays in order, preserving entry IDs. * Golden test ensures same input sequence → same mirror tree hash. --- ## 7) Database notes (Mongo ↔ Postgres switchability) * Use repository interfaces in `StellaOps.Shared.Persistence`. * Canonical/public IDs are hash‑derived; DB keys are implementation‑local. * Never rely on DB sort order for any hash or verdict; always re‑canonicalize before hashing. --- ## 8) Debug checklist (most common slips) * Non‑canonical JSON (unsorted keys, trailing spaces). * Local time sneaking into proofs. * Unstable GUIDs in tests. * Non‑deterministic enumeration over `Dictionary<>`. * Different newline conventions on Windows—enforce `\n` in canonical paths. --- ## 9) Useful commands ```bash # run determinism pack dotnet test --filter Category=Determinism # update golden snapshots (intentional change only) dotnet test --filter Category=Determinism -- TestRunParameters.Parameter(name=\"UpdateSnapshots\", value=\"true\") # quick API smoke curl -s http://localhost:5080/health curl -s -X POST http://localhost:5081/scan -d @samples/nginx.sbom.json # verify DSSE signature locally dotnet run --project tools/StellaOps.Tools.Verify -- file trust.receipt.json ``` --- ## 10) Ask‑once glossary * **SBOM**: software bill of materials (CycloneDX/SPDX). * **VEX**: vulnerability exploitability exchange (verdicts: affected / not‑affected / under‑investigation). * **DSSE**: signed payload wrapper; we canonicalize before signing. * **Lattice**: rule system to merge proofs/verdicts from different sources deterministically. * **GraphRevisionID**: hash of the canonical trust graph; your “build number” for audits. --- Want me to turn this into `docs/onboarding/dev-quickstart.md` plus three ready‑to‑run GitHub issues and the test scaffolds? ````markdown # StellaOps Developer Quickstart > **Audience:** Mid‑level .NET developers > **Goal:** Get you productive on StellaOps in 1–2 days, with special focus on determinism, cryptographic attestations, and the canonical data model. --- ## 1. What You’re Building (Context) StellaOps is a sovereign, air‑gap‑friendly platform that turns **SBOMs → VEX** with a fully **replayable, deterministic trust graph**. Core concepts: - **Deterministic scans:** Same inputs → same graph, hashes, and verdicts. - **Cryptographic attestations:** DSSE/in‑toto envelopes, optional PQC. - **Trust lattice:** Merges vendor VEX, runtime signals, configs, etc. into a single deterministic verdict. - **Audit trail:** Every decision is reproducible from stored inputs and proofs. If you think “content‑addressed trust pipeline for SBOMs + VEX,” you’re in the right mental model. --- ## 2. Repository & Docs Map Start by opening these projects **in order**: 1. `src/StellaOps.Scanner.WebService/` Scanning endpoints, rule plumbing, and calls into the trust lattice. 2. `src/StellaOps.Vexer/` (a.k.a. *Excititor*) VEX verdict engine and trust‑merge logic. 3. `src/StellaOps.Sbomer/` SBOM ingest / normalize (CycloneDX, SPDX). 4. `src/StellaOps.Authority/` Key management, DSSE/in‑toto attestations, license tokens, Rekor integration. 5. `src/StellaOps.Scheduler/` Batch processing, replay orchestration. 6. `src/StellaOps.Shared/CanonicalModel/` Canonical entities & graph IDs. **Read this carefully** – it underpins determinism. Helpful docs: - `docs/modules/platform/*` – protocols (DSSE envelopes, lattice terms, trust receipts). - `docs/architecture/*` – high‑level diagrams and flows (if present). --- ## 3. Local Dev Setup ### 3.1 Prerequisites - **.NET 10 SDK** (preview as specified in repo) - **Docker** (for DB, queues, object storage) - **Node.js** (for Angular UI, if you’re touching the frontend) - **WSL2** (optional, but makes life easier on Windows) ### 3.2 Bring Up Infra From the repo root: ```bash # Bring up core infra for offline / air‑gap friendly dev docker compose -f compose/offline-kit.yml up -d ```` This usually includes: * MongoDB or Postgres (configurable) * RabbitMQ (or equivalent queue) * MinIO / object storage (depending on profile) ### 3.3 Configure Environment Copy the example env and tweak: ```bash cp env/example.local.env .env ``` Key settings: * `STELLAOPS_DB=Mongo` or `Postgres` * `AUTHORITY_*` – key material and config (see comments in `example.local.env`) * Optional: `AUTHORITY_PQC=on` to enable post‑quantum keys (Dilithium). ### 3.4 Build & Run Backend ```bash # Restore & build everything dotnet restore dotnet build -c Debug # Run a focused slice for development dotnet run --project src/StellaOps.Authority/StellaOps.Authority.csproj dotnet run --project src/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj ``` Health checks (adjust ports if needed): ```bash curl -s http://localhost:5080/health # Authority curl -s http://localhost:5081/health # Scanner ``` --- ## 4. Deterministic Sanity Tests These tests prove your local environment is configured correctly for **determinism**. If any of these fail due to snapshot mismatch, fix your environment before writing new features. ### 4.1 SBOM → VEX “Not Affected” (Reachability False) ```bash dotnet test tests/Determinism/Det_SbomToVex_NotAffected.csproj ``` **What it checks:** * Two consecutive runs with the same SBOM produce: * Identical `GraphRevisionID` * Identical DSSE payload hashes If they differ, inspect: * JSON canonicalization * Locale / culture * Line endings --- ### 4.2 In‑toto Chain: Source → Build → Image Attestation ```bash dotnet test tests/Attestations/Att_InToto_Chain.csproj ``` **What it checks:** * DSSE envelope canonicalization is stable. * Signature over CBOR‑canonical JSON matches the stored hash. * Full in‑toto chain can be replayed deterministically. --- ### 4.3 Lattice Merge: Vendor VEX + Runtime Signal ```bash dotnet test tests/Lattice/Lattice_VendorPlusRuntime.csproj ``` **What it checks:** * Merge verdict is stable regardless of input set order. * Resulting `TrustReceipt` is byte‑for‑byte identical between runs. If any “golden” snapshots differ, you likely have: * Non‑canonical JSON * Unstable enumeration (e.g., iterating `Dictionary<>` directly) * Locale or newline drift --- ## 5. Coding Conventions (Determinism & Crypto) These are **non‑negotiable** when working on code that affects trust graphs, proofs, or attestations. ### 5.1 JSON & Canonicalization * Use our **`CanonicalJson`** helper for anything that will be: * Hashed * Signed * Used as a canonical ID input * Rules: * UTF‑8 * Sorted keys * No insignificant whitespace * `\n` line endings (enforced for canonical paths) ### 5.2 DSSE Envelopes * `payloadType` must always be: * `application/vnd.stellaops.trust+json` * Envelopes are signed over the **canonicalized bytes** of the payload. Example: ```csharp var payload = CanonicalJson.Serialize(trustDoc); var env = DsseEnvelope.Create("application/vnd.stellaops.trust+json", payload); var signed = await keyRing.SignAsync(env.CanonicalizeBytes()); await rekor.SubmitAsync(signed, RekorMode.OfflineMirrorIfAirgapped); ``` ### 5.3 Hashing * Internal content addressing: **BLAKE3** * External / interop where required: **SHA‑256** Never mix algorithms for the same ID type. ### 5.4 Keys & Algorithms * Default signatures: **Ed25519** via `Authority.KeyRing` * Optional PQC: **Dilithium** when `AUTHORITY_PQC=on` * Never manage keys directly – always use the keyring abstraction. ### 5.5 Time & Clocks * Use `Instant` (UTC) / `DateTimeOffset` in UTC. * Truncate to **milliseconds** for anything that ends up in canonical data. * Never use `DateTime.Now` or local time in trust‑critical code. ### 5.6 IDs & Graph Nodes * Public / canonical IDs are derived from **hashes of canonical bytes**. * DB primary keys are implementation details; do **not** leak them externally. * Do **not** depend on DB auto‑increment or sort order for anything that affects hashing. ### 5.7 VEX Verdicts Every VEX verdict must: * Be backed by `proofs[]` (e.g., reachability analysis, config guards, runtime path). * Emit a `receipt` signed by **Authority**, wrapping: * Verdict * Proof hashes * Context (component, vulnerability, scope, etc.) --- ## 6. Daily Workflow A minimal loop that keeps you aligned with the platform’s guarantees: 1. **Pick a focused issue** (see starter tasks below). 2. **Write tests first**, especially determinism tests where applicable. 3. Implement the change, keeping: * Canonicalization boundaries explicit * Hashing and signing centralized 4. Run: ```bash dotnet test --filter Category=Determinism ``` 5. Commit using: * `feat(scanner): ...` * `feat(vexer): ...` * `feat(authority): ...` * etc. Include the affected `GraphRevisionID` in the commit body when relevant to trust‑graph changes. --- ## 7. Suggested Starter Tasks These are good first issues that teach the canonical model and determinism expectations. ### 7.1 Normalize CycloneDX Components → Canonical Packages **Goal:** Map CycloneDX `components` to our `CanonicalPackage` model with stable IDs. * **Area:** `StellaOps.Sbomer` * **Tests:** `tests/Determinism/Det_SbomMapping` **Definition of done:** * Two equivalent SBOMs (only field order differs) produce: * Identical package sets * Identical canonical package IDs * `CanonicalPackageSet.hash` is stable. * Edge cases handled: * Missing `purl` * Duplicate components * Case differences in names or versions --- ### 7.2 Implement “Not‑Affected by Configuration” Proof **Goal:** Add proof generator that marks a CVE as `not_affected` when a config gate disables the vulnerable path. * **Area:** `StellaOps.Vexer/Proofs/ConfigSwitchProof.cs` **Definition of done:** * Given `FeatureX=false`, CVE‑1234 yields verdict: * `status = not_affected` * `proofs[]` includes a `ConfigSwitchProof` with: * `configPath` * `observed=false` * Proof is hashed deterministically and included in the DSSE receipt. * Lattice merge behavior: * Vendor verdict: `affected` * Runtime/config proof weight > threshold → merged verdict becomes `not_affected` deterministically. --- ### 7.3 Authority Offline Rekor Mirror Submitter **Goal:** Support air‑gapped mode: DSSE entries are written to a local Rekor mirror and synced later. * **Area:** `StellaOps.Authority/Rekor/RekorMirrorClient.cs` **Definition of done:** * `RekorMode.OfflineMirrorIfAirgapped`: * Stores canonical entries (JSON + hash‑based path) on disk / object store. * A `rekor sync` background job (or CLI) replays entries in order to the real Rekor instance. * Determinism test: * Same input DSSE sequence → same mirror tree hash and Rekor entry order. --- ## 8. Database Notes (Mongo ↔ Postgres) StellaOps is designed to be DB‑agnostic. * Use repository interfaces from `StellaOps.Shared.Persistence`. * Keep **canonical/public IDs hash‑derived**; DB keys are internal. * Do **not** depend on DB sort order for anything that: * Affects hashes * Affects verdicts * Ends up in canonical data If you need ordering, sort **after** canonicalization using deterministic criteria. --- ## 9. Common Pitfalls & Debug Checklist When a determinism test fails, look for these first: 1. **Non‑canonical JSON** * Unsorted keys * Extra whitespace * Mixed `\r\n` vs `\n` line endings 2. **Local Time Leaks** * Any `DateTime.Now` or local time in proofs or receipts. 3. **Random / Unstable IDs** * New GUIDs in tests or canonical entities. * Auto‑increment IDs leaking into hashes. 4. **Unordered Collections** * Iterating over `Dictionary<>` or `HashSet<>` without ordering. * Using LINQ without explicit `OrderBy` when hashing or serializing. 5. **Platform Differences** * Windows vs Linux newline differences. * Locale differences (`,` vs `.` for decimals, etc.) – always use invariant culture for canonical data. --- ## 10. Useful Commands ### 10.1 Determinism Pack ```bash # Run only determinism‑tagged tests dotnet test --filter Category=Determinism ``` Update golden snapshots when you *intend* to change canonical behavior: ```bash dotnet test --filter Category=Determinism -- \ TestRunParameters.Parameter(name="UpdateSnapshots", value="true") ``` ### 10.2 Quick API Smoke ```bash curl -s http://localhost:5080/health curl -s -X POST \ http://localhost:5081/scan \ -H "Content-Type: application/json" \ -d @samples/nginx.sbom.json ``` ### 10.3 Verify DSSE Signature Locally ```bash dotnet run --project tools/StellaOps.Tools.Verify -- file trust.receipt.json ``` --- ## 11. Glossary (Ask‑Once) * **SBOM** – Software Bill of Materials (CycloneDX/SPDX). * **VEX** – Vulnerability Exploitability eXchange: * Verdicts like `affected`, `not_affected`, `under_investigation`. * **DSSE** – Dead Simple Signing Envelope: * Wrapper around payload + signature; we sign canonical bytes. * **In‑toto** – Supply‑chain attestation framework; we use it for source→build→artifact chains. * **Lattice** – Rule system that merges multiple verdicts/proofs into a single deterministic verdict. * **GraphRevisionID** – Hash of the canonical trust graph at a point in time; acts like a build number for audits. --- Welcome aboard. Your best “map” into the system is: 1. Read `CanonicalModel` types. 2. Run the determinism tests. 3. Pick one of the starter tasks and ship a small, well‑tested change. If you keep everything **canonical, hashable, and replayable**, you’ll fit right in. ``` ```