Update module architecture docs and workflow tutorials

- Module dossiers: attestor, authority, cli, graph, scanner
- Policy assistant parameters guide
- UI v2-rewire navigation rendering policy
- Test suite overview update
- Workflow engine requirements and tutorial series (01-08)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-30 17:25:37 +03:00
parent 5722d36c0e
commit a6ffb38ecf
17 changed files with 4442 additions and 4380 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# component_architecture_authority.md — **Stella Ops Authority** (2025Q4) # component_architecture_authority.md **StellaOps Authority** (2025Q4)
> **Current tenant-selection ADR:** `docs/architecture/decisions/ADR-002-multi-tenant-same-api-key-selection.md` > **Current tenant-selection ADR:** `docs/architecture/decisions/ADR-002-multi-tenant-same-api-key-selection.md`
> **Service impact ledger:** `docs/technical/architecture/multi-tenant-service-impact-ledger.md` > **Service impact ledger:** `docs/technical/architecture/multi-tenant-service-impact-ledger.md`
@@ -8,18 +8,18 @@
> Consolidates identity and tenancy requirements documented across the AOC, Policy, and Platform guides, along with the dedicated Authority implementation plan. > Consolidates identity and tenancy requirements documented across the AOC, Policy, and Platform guides, along with the dedicated Authority implementation plan.
> **Scope.** Implementation‑ready architecture for **Stella Ops Authority**: the on‑prem **OIDC/OAuth2** service that issues **short‑lived, sender‑constrained operational tokens (OpToks)** to first‑party services and tools. Covers protocols (DPoP & mTLS binding), token shapes, endpoints, storage, rotation, HA, RBAC, audit, and testing. This component is the trust anchor for *who* is calling inside a Stella Ops installation. (Entitlement is proven separately by **PoE** from the cloud Licensing Service; Authority does not issue PoE.) > **Scope.** Implementationready architecture for **StellaOps Authority**: the onprem **OIDC/OAuth2** service that issues **shortlived, senderconstrained operational tokens (OpToks)** to firstparty services and tools. Covers protocols (DPoP & mTLS binding), token shapes, endpoints, storage, rotation, HA, RBAC, audit, and testing. This component is the trust anchor for *who* is calling inside a StellaOps installation. (Entitlement is proven separately by **PoE** from the cloud Licensing Service; Authority does not issue PoE.)
--- ---
## 0) Mission & boundaries ## 0) Mission & boundaries
**Mission.** Provide **fast, local, verifiable** authentication for Stella Ops microservices and tools by minting **very short‑lived** OAuth2/OIDC tokens that are **sender‑constrained** (DPoP or mTLS‑bound). Support RBAC scopes, multi‑tenant claims, and deterministic validation for APIs (Scanner, Signer, Attestor, Excititor, Concelier, UI, CLI, Zastava). **Mission.** Provide **fast, local, verifiable** authentication for StellaOps microservices and tools by minting **very shortlived** OAuth2/OIDC tokens that are **senderconstrained** (DPoP or mTLSbound). Support RBAC scopes, multitenant claims, and deterministic validation for APIs (Scanner, Signer, Attestor, Excititor, Concelier, UI, CLI, Zastava).
**Boundaries.** **Boundaries.**
* Authority **does not** validate entitlements/licensing. That’s enforced by **Signer** using **PoE** with the cloud Licensing Service. * Authority **does not** validate entitlements/licensing. Thats enforced by **Signer** using **PoE** with the cloud Licensing Service.
* Authority tokens are **operational only** (2–5 min TTL) and must not be embedded in long‑lived artifacts or stored in SBOMs. * Authority tokens are **operational only** (25min TTL) and must not be embedded in longlived artifacts or stored in SBOMs.
* Authority is **stateless for validation** (JWT) and **optional introspection** for services that prefer online checks. * Authority is **stateless for validation** (JWT) and **optional introspection** for services that prefer online checks.
--- ---
@@ -29,16 +29,16 @@
* **OIDC Discovery**: `/.well-known/openid-configuration` * **OIDC Discovery**: `/.well-known/openid-configuration`
* **OAuth2** grant types: * **OAuth2** grant types:
* **Client Credentials** (service↔service, with mTLS or private_key_jwt) * **Client Credentials** (serviceservice, with mTLS or private_key_jwt)
* **Device Code** (CLI login on headless agents; optional) * **Device Code** (CLI login on headless agents; optional)
* **Authorization Code + PKCE** (browser login for UI; optional) * **Authorization Code + PKCE** (browser login for UI; optional)
* **Sender constraint options** (choose per caller or per audience): * **Sender constraint options** (choose per caller or per audience):
* **DPoP** (Demonstration of Proofâ€ofâ€Possession): proof JWT on each HTTP request, bound to the access token via `cnf.jkt`. * **DPoP** (Demonstration of ProofofPossession): proof JWT on each HTTP request, bound to the access token via `cnf.jkt`.
* **OAuth 2.0 mTLS** (certificate‑bound tokens): token bound to client certificate thumbprint via `cnf.x5t#S256`. * **OAuth 2.0 mTLS** (certificatebound tokens): token bound to client certificate thumbprint via `cnf.x5t#S256`.
* **Signing algorithms**: **EdDSA (Ed25519)** preferred; fallback **ES256 (P‑256)**. Rotation is supported via **kid** in JWKS. * **Signing algorithms**: **EdDSA (Ed25519)** preferred; fallback **ES256 (P256)**. Rotation is supported via **kid** in JWKS.
* **Token format**: **JWT** access tokens (compact), optionally opaque reference tokens for services that insist on introspection. * **Token format**: **JWT** access tokens (compact), optionally opaque reference tokens for services that insist on introspection.
* **Clock skew tolerance**: ±60 s; issue `nbf`, `iat`, `exp` accordingly. * **Clock skew tolerance**: ±60s; issue `nbf`, `iat`, `exp` accordingly.
--- ---
@@ -47,7 +47,7 @@
* **Incident mode tokens** require the `obs:incident` scope, a human-supplied `incident_reason`, and remain valid only while `auth_time` stays within a five-minute freshness window. Resource servers enforce the same window and persist `incident.reason`, `incident.auth_time`, and the fresh-auth verdict in `authority.resource.authorize` events. Authority exposes `/authority/audit/incident` so auditors can review recent activations. * **Incident mode tokens** require the `obs:incident` scope, a human-supplied `incident_reason`, and remain valid only while `auth_time` stays within a five-minute freshness window. Resource servers enforce the same window and persist `incident.reason`, `incident.auth_time`, and the fresh-auth verdict in `authority.resource.authorize` events. Authority exposes `/authority/audit/incident` so auditors can review recent activations.
### 2.1 Access token (OpTok) — short‑lived (120–300 s) ### 2.1 Access token (OpTok) shortlived (120300s)
**Registered claims** **Registered claims**
@@ -62,7 +62,7 @@ jti = <uuid>
scope = "scanner.scan scanner.export signer.sign ..." scope = "scanner.scan scanner.export signer.sign ..."
``` ```
**Sender‑constraint (`cnf`)** **Senderconstraint (`cnf`)**
* **DPoP**: * **DPoP**:
@@ -84,11 +84,11 @@ roles = [ "svc.scanner", "svc.signer", "ui.admin", ... ]
plan? = <plan name> // optional hint for UIs; not used for enforcement plan? = <plan name> // optional hint for UIs; not used for enforcement
``` ```
> **Note**: Do **not** copy PoE claims into OpTok; OpTok ≠ entitlement. Only **Signer** checks PoE. > **Note**: Do **not** copy PoE claims into OpTok; OpTok entitlement. Only **Signer** checks PoE.
### 2.2 Refresh tokens (optional) ### 2.2 Refresh tokens (optional)
* Default **disabled**. If enabled (for UI interactive logins), pair with **DPoP‑bound** refresh tokens or **mTLS** client sessions; short TTL (≤ 8 h), rotating on use (replay‑safe). * Default **disabled**. If enabled (for UI interactive logins), pair with **DPoPbound** refresh tokens or **mTLS** client sessions; short TTL (≤ 8h), rotating on use (replaysafe).
### 2.3 ID tokens (optional) ### 2.3 ID tokens (optional)
@@ -100,8 +100,8 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 3.1 OIDC discovery & keys ### 3.1 OIDC discovery & keys
* `GET /.well-known/openid-configuration` → endpoints, algs, jwks_uri * `GET /.well-known/openid-configuration` endpoints, algs, jwks_uri
* `GET /jwks` → JSON Web Key Set (rotating, at least 2 active keys during transition) * `GET /jwks` JSON Web Key Set (rotating, at least 2 active keys during transition)
> **KMS-backed keys.** When the signing provider is `kms`, Authority fetches only the public coordinates (`Qx`, `Qy`) and version identifiers from the backing KMS. Private scalars never leave the provider; JWKS entries are produced by re-exporting the public material via the `kms.version` metadata attached to each key. Retired keys keep the same `kms.version` metadata so audits can trace which cloud KMS version produced a token. > **KMS-backed keys.** When the signing provider is `kms`, Authority fetches only the public coordinates (`Qx`, `Qy`) and version identifiers from the backing KMS. Private scalars never leave the provider; JWKS entries are produced by re-exporting the public material via the `kms.version` metadata attached to each key. Retired keys keep the same `kms.version` metadata so audits can trace which cloud KMS version produced a token.
@@ -111,12 +111,12 @@ plan? = <plan name> // optional hint for UIs; not used for e
> Legacy aliases under `/oauth/token` are deprecated as of 1 November 2025 and now emit `Deprecation/Sunset/Warning` headers. See [`docs/api/authority-legacy-auth-endpoints.md`](../../api/authority-legacy-auth-endpoints.md) for timelines and migration guidance. > Legacy aliases under `/oauth/token` are deprecated as of 1 November 2025 and now emit `Deprecation/Sunset/Warning` headers. See [`docs/api/authority-legacy-auth-endpoints.md`](../../api/authority-legacy-auth-endpoints.md) for timelines and migration guidance.
* **Client Credentials** (service→service): * **Client Credentials** (serviceservice):
* **mTLS**: mutual TLS + `client_id` → bound token (`cnf.x5t#S256`) * **mTLS**: mutual TLS + `client_id` bound token (`cnf.x5t#S256`)
* `security.senderConstraints.mtls.enforceForAudiences` forces the mTLS path when requested `aud`/`resource` values intersect high-value audiences (defaults include `signer`). Authority rejects clients attempting to use DPoP/basic secrets for these audiences. * `security.senderConstraints.mtls.enforceForAudiences` forces the mTLS path when requested `aud`/`resource` values intersect high-value audiences (defaults include `signer`). Authority rejects clients attempting to use DPoP/basic secrets for these audiences.
* Stored `certificateBindings` are authoritative: thumbprint, subject, issuer, serial number, and SAN values are matched against the presented certificate, with rotation grace applied to activation windows. Failures surface deterministic error codes (e.g. `certificate_binding_subject_mismatch`). * Stored `certificateBindings` are authoritative: thumbprint, subject, issuer, serial number, and SAN values are matched against the presented certificate, with rotation grace applied to activation windows. Failures surface deterministic error codes (e.g. `certificate_binding_subject_mismatch`).
* **private_key_jwt**: JWT‑based client auth + **DPoP** header (preferred for tools and CLI) * **private_key_jwt**: JWTbased client auth + **DPoP** header (preferred for tools and CLI)
* **Device Code** (CLI): `POST /oauth/device/code` + `POST /oauth/token` poll * **Device Code** (CLI): `POST /oauth/device/code` + `POST /oauth/token` poll
* **Authorization Code + PKCE** (UI): standard * **Authorization Code + PKCE** (UI): standard
@@ -134,7 +134,7 @@ plan? = <plan name> // optional hint for UIs; not used for e
signed with the DPoP private key; header carries JWK. signed with the DPoP private key; header carries JWK.
3. Authority validates proof; issues access token with `cnf.jkt=<thumbprint(JWK)>`. 3. Authority validates proof; issues access token with `cnf.jkt=<thumbprint(JWK)>`.
4. Client uses the same DPoP key to sign **every subsequent API request** to services (Signer, Scanner, …). 4. Client uses the same DPoP key to sign **every subsequent API request** to services (Signer, Scanner, ).
**mTLS flow** **mTLS flow**
@@ -142,11 +142,11 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 3.3 Introspection & revocation (optional) ### 3.3 Introspection & revocation (optional)
* `POST /introspect` → `{ active, sub, scope, aud, exp, cnf, ... }` * `POST /introspect` `{ active, sub, scope, aud, exp, cnf, ... }`
* `POST /revoke` → revokes refresh tokens or opaque access tokens. * `POST /revoke` revokes refresh tokens or opaque access tokens.
> Requests targeting the legacy `/oauth/{introspect|revoke}` paths receive deprecation headers and are scheduled for removal after 1 May 2026. > Requests targeting the legacy `/oauth/{introspect|revoke}` paths receive deprecation headers and are scheduled for removal after 1 May 2026.
* **Replay prevention**: maintain **DPoP `jti` cache** (TTL ≤ 10 min) to reject duplicate proofs when services supply DPoP nonces (Signer requires nonce for high‑value operations). * **Replay prevention**: maintain **DPoP `jti` cache** (TTL 10 min) to reject duplicate proofs when services supply DPoP nonces (Signer requires nonce for highvalue operations).
### 3.4 UserInfo (optional for UI) ### 3.4 UserInfo (optional for UI)
@@ -156,19 +156,19 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 3.5 Vuln Explorer workflow safeguards ### 3.5 Vuln Explorer workflow safeguards
* **Anti-forgery flow** — Vuln Explorer’s mutation verbs call * **Anti-forgery flow** Vuln Explorers mutation verbs call
* `POST /vuln/workflow/anti-forgery/issue` * `POST /vuln/workflow/anti-forgery/issue`
* `POST /vuln/workflow/anti-forgery/verify` * `POST /vuln/workflow/anti-forgery/verify`
Callers must hold `vuln:operate` scopes. Issued tokens embed the actor, tenant, whitelisted actions, ABAC selectors (environment/owner/business tier), and optional context key/value pairs. Tokens are EdDSA/ES256 signed via the primary Authority signing key and default to a 10‑minute TTL (cap: 30 minutes). Verification enforces nonce reuse prevention, tenant match, and action membership before forwarding the request to Vuln Explorer. Callers must hold `vuln:operate` scopes. Issued tokens embed the actor, tenant, whitelisted actions, ABAC selectors (environment/owner/business tier), and optional context key/value pairs. Tokens are EdDSA/ES256 signed via the primary Authority signing key and default to a 10minute TTL (cap: 30minutes). Verification enforces nonce reuse prevention, tenant match, and action membership before forwarding the request to Vuln Explorer.
* **Attachment access** — Evidence bundles and attachments reference a ledger hash. Vuln Explorer obtains a scoped download token through: * **Attachment access** Evidence bundles and attachments reference a ledger hash. Vuln Explorer obtains a scoped download token through:
* `POST /vuln/attachments/tokens/issue` * `POST /vuln/attachments/tokens/issue`
* `POST /vuln/attachments/tokens/verify` * `POST /vuln/attachments/tokens/verify`
These tokens bind the ledger event hash, attachment identifier, optional finding/content metadata, and the actor. They default to a 30‑minute TTL (cap: 4 hours) and require `vuln:investigate`. These tokens bind the ledger event hash, attachment identifier, optional finding/content metadata, and the actor. They default to a 30minute TTL (cap: 4hours) and require `vuln:investigate`.
* **Audit trail** — Both flows emit `vuln.workflow.csrf.*` and `vuln.attachment.token.*` audit records with tenant, actor, ledger hash, nonce, and filtered context metadata so Offline Kit operators can reconcile actions against ledger entries. * **Audit trail** Both flows emit `vuln.workflow.csrf.*` and `vuln.attachment.token.*` audit records with tenant, actor, ledger hash, nonce, and filtered context metadata so Offline Kit operators can reconcile actions against ledger entries.
* **Configuration** * **Configuration**
@@ -200,7 +200,7 @@ plan? = <plan name> // optional hint for UIs; not used for e
### 4.1 Audiences ### 4.1 Audiences
* `signer` — only the **Signer** service should accept tokens with `aud=signer`. * `signer` only the **Signer** service should accept tokens with `aud=signer`.
* `attestor`, `scanner`, `concelier`, `excititor`, `ui`, `zastava` similarly. * `attestor`, `scanner`, `concelier`, `excititor`, `ui`, `zastava` similarly.
Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their policy. Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their policy.
@@ -227,13 +227,13 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
| `authority:branding.read` / `authority:branding.write` | Authority | Branding admin | | `authority:branding.read` / `authority:branding.write` | Authority | Branding admin |
| `zastava.emit` / `zastava.enforce` | Scanner/Zastava | Runtime events / admission | | `zastava.emit` / `zastava.enforce` | Scanner/Zastava | Runtime events / admission |
**Roles → scopes mapping** is configured centrally (Authority policy) and pushed during token issuance. **Roles scopes mapping** is configured centrally (Authority policy) and pushed during token issuance.
--- ---
## 5) Storage & state ## 5) Storage & state
* **Configuration DB** (PostgreSQL/MySQL): clients, audiences, role→scope maps, tenant/installation registry, device code grants, persistent consents (if any). * **Configuration DB** (PostgreSQL/MySQL): clients, audiences, rolescope maps, tenant/installation registry, device code grants, persistent consents (if any).
* **Cache** (Valkey): * **Cache** (Valkey):
* DPoP **jti** replay cache (short TTL) * DPoP **jti** replay cache (short TTL)
@@ -247,20 +247,20 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
* Maintain **at least 2 signing keys** active during rotation; tokens carry `kid`. * Maintain **at least 2 signing keys** active during rotation; tokens carry `kid`.
* Prefer **Ed25519** for compact tokens; maintain **ES256** fallback for FIPS contexts. * Prefer **Ed25519** for compact tokens; maintain **ES256** fallback for FIPS contexts.
* Rotation cadence: 30–90 days; emergency rotation supported. * Rotation cadence: 3090 days; emergency rotation supported.
* Publish new JWKS **before** issuing tokens with the new `kid` to avoid cold‑start validation misses. * Publish new JWKS **before** issuing tokens with the new `kid` to avoid coldstart validation misses.
* Keep **old keys** available **at least** for max token TTL + 5 minutes. * Keep **old keys** available **at least** for max token TTL + 5 minutes.
--- ---
## 7) HA & performance ## 7) HA & performance
* **Stateless issuance** (except device codes/refresh) → scale horizontally behind a load‑balancer. * **Stateless issuance** (except device codes/refresh) scale horizontally behind a loadbalancer.
* **DB** only for client metadata and optional flows; token checks are JWT‑local; introspection endpoints hit cache/DB minimally. * **DB** only for client metadata and optional flows; token checks are JWTlocal; introspection endpoints hit cache/DB minimally.
* **Targets**: * **Targets**:
* Token issuance P95 ≤ **20 ms** under warm cache. * Token issuance P95 **20ms** under warm cache.
* DPoP proof validation ≤ **1 ms** extra per request at resource servers (Signer/Scanner). * DPoP proof validation **1ms** extra per request at resource servers (Signer/Scanner).
* 99.9% uptime; HPA on CPU/latency. * 99.9% uptime; HPA on CPU/latency.
--- ---
@@ -269,7 +269,7 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
* **Strict TLS** (1.3 preferred); HSTS; modern cipher suites. * **Strict TLS** (1.3 preferred); HSTS; modern cipher suites.
* **mTLS** enabled where required (Signer/Attestor paths). * **mTLS** enabled where required (Signer/Attestor paths).
* **Replay protection**: DPoP `jti` cache, nonce support for **Signer** (add `DPoP-Nonce` header on 401; clients re‑sign). * **Replay protection**: DPoP `jti` cache, nonce support for **Signer** (add `DPoP-Nonce` header on 401; clients resign).
* **Rate limits** per client & per IP; exponential backoff on failures. * **Rate limits** per client & per IP; exponential backoff on failures.
* **Secrets**: clients use **private_key_jwt** or **mTLS**; never basic secrets over the wire. * **Secrets**: clients use **private_key_jwt** or **mTLS**; never basic secrets over the wire.
* **CSP/CSRF** hardening on UI flows; `SameSite=Lax` cookies; PKCE enforced. * **CSP/CSRF** hardening on UI flows; `SameSite=Lax` cookies; PKCE enforced.
@@ -277,10 +277,10 @@ Services **must** verify `aud` and **sender constraint** (DPoP/mTLS) per their p
--- ---
## 9) Multi‑tenancy & installations ## 9) Multitenancy & installations
* **Tenant (`tid`)** and **Installation (`inst`)** registries define which audiences/scopes a client can request. * **Tenant (`tid`)** and **Installation (`inst`)** registries define which audiences/scopes a client can request.
* Cross‑tenant isolation enforced at issuance (disallow rogue `aud`), and resource servers **must** check that `tid` matches their configured tenant. * Crosstenant isolation enforced at issuance (disallow rogue `aud`), and resource servers **must** check that `tid` matches their configured tenant.
--- ---
@@ -293,7 +293,7 @@ Authority exposes two admin tiers:
``` ```
POST /admin/clients # create/update client (confidential/public) POST /admin/clients # create/update client (confidential/public)
POST /admin/audiences # register audience resource URIs POST /admin/audiences # register audience resource URIs
POST /admin/roles # define role→scope mappings POST /admin/roles # define rolescope mappings
POST /admin/tenants # create tenant/install entries POST /admin/tenants # create tenant/install entries
POST /admin/keys/rotate # rotate signing key (zero-downtime) POST /admin/keys/rotate # rotate signing key (zero-downtime)
GET /admin/metrics # Prometheus exposition (token issue rates, errors) GET /admin/metrics # Prometheus exposition (token issue rates, errors)
@@ -306,10 +306,10 @@ Declared client `audiences` flow through to the issued JWT `aud` claim and the t
## 11) Integration hard lines (what resource servers must enforce) ## 11) Integration hard lines (what resource servers must enforce)
Every Stella Ops service that consumes Authority tokens **must**: Every StellaOps service that consumes Authority tokens **must**:
1. Verify JWT signature (`kid` in JWKS), `iss`, `aud`, `exp`, `nbf`. 1. Verify JWT signature (`kid` in JWKS), `iss`, `aud`, `exp`, `nbf`.
2. Enforce **sender‑constraint**: 2. Enforce **senderconstraint**:
* **DPoP**: validate DPoP proof (`htu`, `htm`, `iat`, `jti`) and match `cnf.jkt`; cache `jti` for replay defense; honor nonce challenges. * **DPoP**: validate DPoP proof (`htu`, `htm`, `iat`, `jti`) and match `cnf.jkt`; cache `jti` for replay defense; honor nonce challenges.
* **mTLS**: match presented client cert thumbprint to token `cnf.x5t#S256`. * **mTLS**: match presented client cert thumbprint to token `cnf.x5t#S256`.
@@ -322,8 +322,8 @@ Every Stella Ops service that consumes Authority tokens **must**:
## 12) Error surfaces & UX ## 12) Error surfaces & UX
* Token endpoint errors follow OAuth2 (`invalid_client`, `invalid_grant`, `invalid_scope`, `unauthorized_client`). * Token endpoint errors follow OAuth2 (`invalid_client`, `invalid_grant`, `invalid_scope`, `unauthorized_client`).
* Resource servers use RFC 6750 style (`WWW-Authenticate: DPoP error="invalid_token", error_description="…", dpop_nonce="…" `). * Resource servers use RFC6750 style (`WWW-Authenticate: DPoP error="invalid_token", error_description="", dpop_nonce="" `).
* For DPoP nonce challenges, clients retry with the server‑supplied nonce once. * For DPoP nonce challenges, clients retry with the serversupplied nonce once.
--- ---
@@ -431,8 +431,8 @@ authority:
* **JWT validation**: wrong `aud`, expired `exp`, skewed `nbf`, stale `kid`. * **JWT validation**: wrong `aud`, expired `exp`, skewed `nbf`, stale `kid`.
* **DPoP**: invalid `htu`/`htm`, replayed `jti`, stale `iat`, wrong `jkt`, nonce dance. * **DPoP**: invalid `htu`/`htm`, replayed `jti`, stale `iat`, wrong `jkt`, nonce dance.
* **mTLS**: wrong client cert, wrong CA, thumbprint mismatch. * **mTLS**: wrong client cert, wrong CA, thumbprint mismatch.
* **RBAC**: scope enforcement per audience; over‑privileged client denied. * **RBAC**: scope enforcement per audience; overprivileged client denied.
* **Rotation**: JWKS rotation while load‑testing; zero‑downtime verification. * **Rotation**: JWKS rotation while loadtesting; zerodowntime verification.
* **HA**: kill one Authority instance; verify issuance continues; JWKS served by peers. * **HA**: kill one Authority instance; verify issuance continues; JWKS served by peers.
* **Performance**: 1k token issuance/sec on 2 cores with Valkey enabled for jti caching. * **Performance**: 1k token issuance/sec on 2 cores with Valkey enabled for jti caching.
@@ -442,18 +442,18 @@ authority:
| Threat | Vector | Mitigation | | Threat | Vector | Mitigation |
| ------------------- | ---------------- | ------------------------------------------------------------------------------------------ | | ------------------- | ---------------- | ------------------------------------------------------------------------------------------ |
| Token theft | Copy of JWT | **Short TTL**, **sender‑constraint** (DPoP/mTLS); replay blocked by `jti` cache and nonces | | Token theft | Copy of JWT | **Short TTL**, **senderconstraint** (DPoP/mTLS); replay blocked by `jti` cache and nonces |
| Replay across hosts | Reuse DPoP proof | Enforce `htu`/`htm`, `iat` freshness, `jti` uniqueness; services may require **nonce** | | Replay across hosts | Reuse DPoP proof | Enforce `htu`/`htm`, `iat` freshness, `jti` uniqueness; services may require **nonce** |
| Impersonation | Fake client | mTLS or `private_key_jwt` with pinned JWK; client registration & rotation | | Impersonation | Fake client | mTLS or `private_key_jwt` with pinned JWK; client registration & rotation |
| Key compromise | Signing key leak | HSM/KMS storage, key rotation, audit; emergency key revoke path; narrow token TTL | | Key compromise | Signing key leak | HSM/KMS storage, key rotation, audit; emergency key revoke path; narrow token TTL |
| Cross‑tenant abuse | Scope elevation | Enforce `aud`, `tid`, `inst` at issuance and resource servers | | Crosstenant abuse | Scope elevation | Enforce `aud`, `tid`, `inst` at issuance and resource servers |
| Downgrade to bearer | Strip DPoP | Resource servers require DPoP/mTLS based on `aud`; reject bearer without `cnf` | | Downgrade to bearer | Strip DPoP | Resource servers require DPoP/mTLS based on `aud`; reject bearer without `cnf` |
--- ---
## 17) Deployment & HA ## 17) Deployment & HA
* **Stateless** microservice, containerized; run ≥ 2 replicas behind LB. * **Stateless** microservice, containerized; run 2 replicas behind LB.
* **DB**: HA Postgres (or MySQL) for clients/roles; **Valkey** for device codes, DPoP nonces/jtis. * **DB**: HA Postgres (or MySQL) for clients/roles; **Valkey** for device codes, DPoP nonces/jtis.
* **Secrets**: mount client JWKs via K8s Secrets/HashiCorp Vault; signing keys via KMS. * **Secrets**: mount client JWKs via K8s Secrets/HashiCorp Vault; signing keys via KMS.
* **Backups**: DB daily; Valkey not critical (ephemeral). * **Backups**: DB daily; Valkey not critical (ephemeral).
@@ -470,7 +470,7 @@ authority:
--- ---
## 19) Quick reference — wire examples ## 19) Quick reference wire examples
**Access token (payload excerpt)** **Access token (payload excerpt)**
@@ -507,10 +507,10 @@ Signer validates that `hash(JWK)` in the proof matches `cnf.jkt` in the token.
## 20) Rollout plan ## 20) Rollout plan
1. **MVP**: Client Credentials (private_key_jwt + DPoP), JWKS, short OpToks, per‑audience scopes. 1. **MVP**: Client Credentials (private_key_jwt + DPoP), JWKS, short OpToks, peraudience scopes.
2. **Add**: mTLS‑bound tokens for Signer/Attestor; device code for CLI; optional introspection. 2. **Add**: mTLSbound tokens for Signer/Attestor; device code for CLI; optional introspection.
3. **Hardening**: DPoP nonce support; full audit pipeline; HA tuning. 3. **Hardening**: DPoP nonce support; full audit pipeline; HA tuning.
4. **UX**: Tenant/installation admin UI; role→scope editors; client bootstrap wizards. 4. **UX**: Tenant/installation admin UI; rolescope editors; client bootstrap wizards.
--- ---

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,40 @@
# Graph architecture # Graph architecture
> Derived from Epic 5 – SBOM Graph Explorer; this section captures the core model, pipeline, and API expectations. Extend with diagrams as implementation matures. > Derived from Epic5 SBOM Graph Explorer; this section captures the core model, pipeline, and API expectations. Extend with diagrams as implementation matures.
## 1) Core model ## 1) Core model
- **Nodes:** - **Nodes:**
- `Artifact` (application/image digest) with metadata (tenant, environment, labels). - `Artifact` (application/image digest) with metadata (tenant, environment, labels).
- `SBOM` (sbom digest, format, version/sequence, chain id). - `SBOM` (sbom digest, format, version/sequence, chain id).
- `Component` (package/version, purl, ecosystem). - `Component` (package/version, purl, ecosystem).
- `File`/`Path` (source files, binary paths) with hash/time metadata. - `File`/`Path` (source files, binary paths) with hash/time metadata.
- `License` nodes linked to components and SBOM attestations. - `License` nodes linked to components and SBOM attestations.
- `Advisory` and `VEXStatement` nodes linking to Concelier/Excititor records via digests. - `Advisory` and `VEXStatement` nodes linking to Concelier/Excititor records via digests.
- `PolicyVersion` nodes representing signed policy packs. - `PolicyVersion` nodes representing signed policy packs.
- **Edges:** directed, timestamped relationships such as `DEPENDS_ON`, `BUILT_FROM`, `DECLARED_IN`, `AFFECTED_BY`, `VEX_EXEMPTS`, `GOVERNS_WITH`, `OBSERVED_RUNTIME`, `SBOM_VERSION_OF`, and `SBOM_LINEAGE_*`. Each edge carries provenance (SRM hash, SBOM digest, policy run ID). - **Edges:** directed, timestamped relationships such as `DEPENDS_ON`, `BUILT_FROM`, `DECLARED_IN`, `AFFECTED_BY`, `VEX_EXEMPTS`, `GOVERNS_WITH`, `OBSERVED_RUNTIME`, `SBOM_VERSION_OF`, and `SBOM_LINEAGE_*`. Each edge carries provenance (SRM hash, SBOM digest, policy run ID).
- **Overlays:** computed index tables providing fast access to reachability, blast radius, and differential views (e.g., `graph_overlay/vuln/{tenant}/{advisoryKey}`). Runtime endpoints emit overlays inline (`policy.overlay.v1`, `openvex.v1`) with deterministic overlay IDs (`sha256(tenant|nodeId|overlayKind)`) and sampled explain traces on policy overlays. - **Overlays:** computed index tables providing fast access to reachability, blast radius, and differential views (e.g., `graph_overlay/vuln/{tenant}/{advisoryKey}`). Runtime endpoints emit overlays inline (`policy.overlay.v1`, `openvex.v1`) with deterministic overlay IDs (`sha256(tenant|nodeId|overlayKind)`) and sampled explain traces on policy overlays.
## 2) Pipelines ## 2) Pipelines
1. **Ingestion:** Cartographer/SBOM Service emit SBOM snapshots (`sbom_snapshot` events) captured by the Graph Indexer. Ledger lineage references become `SBOM_VERSION_OF` + `SBOM_LINEAGE_*` edges. Advisories/VEX from Concelier/Excititor generate edge updates, policy runs attach overlay metadata. 1. **Ingestion:** Cartographer/SBOM Service emit SBOM snapshots (`sbom_snapshot` events) captured by the Graph Indexer. Ledger lineage references become `SBOM_VERSION_OF` + `SBOM_LINEAGE_*` edges. Advisories/VEX from Concelier/Excititor generate edge updates, policy runs attach overlay metadata.
2. **ETL:** Normalises nodes/edges into canonical IDs, deduplicates, enforces tenant partitions, and writes to the graph store (planned: Neo4j-compatible or PostgreSQL adjacency lists). 2. **ETL:** Normalises nodes/edges into canonical IDs, deduplicates, enforces tenant partitions, and writes to the graph store (planned: Neo4j-compatible or PostgreSQL adjacency lists).
3. **Overlay computation:** Batch workers build materialised views for frequently used queries (impact lists, saved queries, policy overlays) and store as immutable blobs for Offline Kit exports. 3. **Overlay computation:** Batch workers build materialised views for frequently used queries (impact lists, saved queries, policy overlays) and store as immutable blobs for Offline Kit exports.
4. **Diffing:** `graph_diff` jobs compare two snapshots (e.g., pre/post deploy) and generate signed diff manifests for UI/CLI consumption. 4. **Diffing:** `graph_diff` jobs compare two snapshots (e.g., pre/post deploy) and generate signed diff manifests for UI/CLI consumption.
5. **Analytics (Runtime & Signals 140.A):** background workers run Louvain-style clustering + degree/betweenness approximations on ingested graphs, emitting overlays per tenant/snapshot and writing cluster ids back to nodes when enabled. 5. **Analytics (Runtime & Signals 140.A):** background workers run Louvain-style clustering + degree/betweenness approximations on ingested graphs, emitting overlays per tenant/snapshot and writing cluster ids back to nodes when enabled.
## 3) APIs ## 3) APIs
- `POST /graph/search` — NDJSON node tiles with cursor paging, tenant + scope guards. - `POST /graph/search` NDJSON node tiles with cursor paging, tenant + scope guards.
- `POST /graph/query` — NDJSON nodes/edges/stats/cursor with budgets (tiles/nodes/edges) and optional inline overlays (`includeOverlays=true`) emitting `policy.overlay.v1` and `openvex.v1` payloads; overlay IDs are `sha256(tenant|nodeId|overlayKind)`; policy overlay may include a sampled `explainTrace`. - `POST /graph/query` NDJSON nodes/edges/stats/cursor with budgets (tiles/nodes/edges) and optional inline overlays (`includeOverlays=true`) emitting `policy.overlay.v1` and `openvex.v1` payloads; overlay IDs are `sha256(tenant|nodeId|overlayKind)`; policy overlay may include a sampled `explainTrace`.
- `POST /graph/paths` — bounded BFS (depth ≤6) returning path nodes/edges/stats; honours budgets and overlays. - `POST /graph/paths` bounded BFS (depth ≤6) returning path nodes/edges/stats; honours budgets and overlays.
- `POST /graph/diff` — compares `snapshotA` vs `snapshotB`, streaming node/edge added/removed/changed tiles plus stats; budget enforcement mirrors `/graph/query`. - `POST /graph/diff` compares `snapshotA` vs `snapshotB`, streaming node/edge added/removed/changed tiles plus stats; budget enforcement mirrors `/graph/query`.
- `POST /graph/export` — async job producing deterministic manifests (`sha256`, size, format) for `ndjson/csv/graphml/png/svg`; download via `/graph/export/{jobId}`. - `POST /graph/export` async job producing deterministic manifests (`sha256`, size, format) for `ndjson/csv/graphml/png/svg`; download via `/graph/export/{jobId}`.
- `POST /graph/lineage` - returns SBOM lineage nodes/edges anchored by `artifactDigest` or `sbomDigest`, with optional relationship filters and depth limits. - `POST /graph/lineage` - returns SBOM lineage nodes/edges anchored by `artifactDigest` or `sbomDigest`, with optional relationship filters and depth limits.
- **Edge Metadata API** (added 2025-01): - **Edge Metadata API** (added 2025-01):
- `POST /graph/edges/metadata` — batch query for edge explanations; request contains `EdgeIds[]`, response includes `EdgeTileWithMetadata[]` with full provenance. - `POST /graph/edges/metadata` — batch query for edge explanations; request contains `EdgeIds[]`, response includes `EdgeTileWithMetadata[]` with full provenance.
- `GET /graph/edges/{edgeId}/metadata` — single edge metadata with explanation, via, provenance, and evidence references. - `GET /graph/edges/{edgeId}/metadata` — single edge metadata with explanation, via, provenance, and evidence references.
- `GET /graph/edges/path/{sourceNodeId}/{targetNodeId}` — returns all edges on the shortest path between two nodes, each with metadata. - `GET /graph/edges/path/{sourceNodeId}/{targetNodeId}` — returns all edges on the shortest path between two nodes, each with metadata.
- `GET /graph/edges/by-reason/{reason}` — query edges by `EdgeReason` enum (e.g., `SbomDependency`, `AdvisoryAffects`, `VexStatement`, `RuntimeTrace`). - `GET /graph/edges/by-reason/{reason}` — query edges by `EdgeReason` enum (e.g., `SbomDependency`, `AdvisoryAffects`, `VexStatement`, `RuntimeTrace`).
- `GET /graph/edges/by-evidence?evidenceType=&evidenceRef=` — query edges by evidence reference. - `GET /graph/edges/by-evidence?evidenceType=&evidenceRef=` — query edges by evidence reference.
- Legacy: `GET /graph/nodes/{id}`, `POST /graph/query/saved`, `GET /graph/impact/{advisoryKey}`, `POST /graph/overlay/policy` remain in spec but should align to the NDJSON surfaces above as they are brought forward. - Legacy: `GET /graph/nodes/{id}`, `POST /graph/query/saved`, `GET /graph/impact/{advisoryKey}`, `POST /graph/overlay/policy` remain in spec but should align to the NDJSON surfaces above as they are brought forward.
@@ -54,11 +54,11 @@
- Rate limiting and audit logging use the resolved tenant context; authenticated flows no longer collapse to ambiguous `"unknown"` tenant keys. - Rate limiting and audit logging use the resolved tenant context; authenticated flows no longer collapse to ambiguous `"unknown"` tenant keys.
### 3.2) Edge Metadata Contracts ### 3.2) Edge Metadata Contracts
The edge metadata system provides explainability for graph relationships: The edge metadata system provides explainability for graph relationships:
- **EdgeReason** enum: `Unknown`, `SbomDependency`, `StaticSymbol`, `RuntimeTrace`, `PackageManifest`, `Lockfile`, `BuildArtifact`, `ImageLayer`, `AdvisoryAffects`, `VexStatement`, `PolicyOverlay`, `AttestationRef`, `OperatorAnnotation`, `TransitiveInference`, `Provenance`. - **EdgeReason** enum: `Unknown`, `SbomDependency`, `StaticSymbol`, `RuntimeTrace`, `PackageManifest`, `Lockfile`, `BuildArtifact`, `ImageLayer`, `AdvisoryAffects`, `VexStatement`, `PolicyOverlay`, `AttestationRef`, `OperatorAnnotation`, `TransitiveInference`, `Provenance`.
- **EdgeVia** record: Describes how the edge was discovered (method, version, timestamp, confidence in basis points, evidence reference). - **EdgeVia** record: Describes how the edge was discovered (method, version, timestamp, confidence in basis points, evidence reference).
- **EdgeExplanationPayload** record: Full explanation including reason, via, human-readable summary, evidence list, provenance reference, and tags. - **EdgeExplanationPayload** record: Full explanation including reason, via, human-readable summary, evidence list, provenance reference, and tags.
- **EdgeProvenanceRef** record: Source system, collection timestamp, SBOM digest, scan digest, attestation ID, event offset. - **EdgeProvenanceRef** record: Source system, collection timestamp, SBOM digest, scan digest, attestation ID, event offset.
- **EdgeTileWithMetadata** record: Extends `EdgeTile` with `Explanation` property containing the full metadata. - **EdgeTileWithMetadata** record: Extends `EdgeTile` with `Explanation` property containing the full metadata.
@@ -72,34 +72,34 @@ The edge metadata system provides explainability for graph relationships:
- This rollout localizes selected error paths (for example, edge/export not found, invalid reason, and tenant/auth validation text) for `en-US` and `de-DE`. - This rollout localizes selected error paths (for example, edge/export not found, invalid reason, and tenant/auth validation text) for `en-US` and `de-DE`.
## 4) Storage considerations ## 4) Storage considerations
- Backed by either: - Backed by either:
- **Relational + adjacency** (PostgreSQL tables `graph_nodes`, `graph_edges`, `graph_overlays`) with deterministic ordering and streaming exports. - **Relational + adjacency** (PostgreSQL tables `graph_nodes`, `graph_edges`, `graph_overlays`) with deterministic ordering and streaming exports.
- Or **Graph DB** (e.g., Neo4j/Cosmos Gremlin) behind an abstraction layer; choice depends on deployment footprint. - Or **Graph DB** (e.g., Neo4j/Cosmos Gremlin) behind an abstraction layer; choice depends on deployment footprint.
- All storages require tenant partitioning, append-only change logs, and export manifests for Offline Kits. - All storages require tenant partitioning, append-only change logs, and export manifests for Offline Kits.
## 5) Offline & export ## 5) Offline & export
- Each snapshot packages `nodes.jsonl`, `edges.jsonl`, `overlays/` plus manifest with hash, counts, and provenance. Export Center consumes these artefacts for graph-specific bundles. - Each snapshot packages `nodes.jsonl`, `edges.jsonl`, `overlays/` plus manifest with hash, counts, and provenance. Export Center consumes these artefacts for graph-specific bundles.
- Saved queries and overlays include deterministic IDs so Offline Kit consumers can import and replay results. - Saved queries and overlays include deterministic IDs so Offline Kit consumers can import and replay results.
- Runtime hosts register the SBOM ingest pipeline via `services.AddSbomIngestPipeline(...)`. Snapshot exports default to `./artifacts/graph-snapshots` but can be redirected with `STELLAOPS_GRAPH_SNAPSHOT_DIR` or the `SbomIngestOptions.SnapshotRootDirectory` callback. - Runtime hosts register the SBOM ingest pipeline via `services.AddSbomIngestPipeline(...)`. Snapshot exports default to `./artifacts/graph-snapshots` but can be redirected with `STELLAOPS_GRAPH_SNAPSHOT_DIR` or the `SbomIngestOptions.SnapshotRootDirectory` callback.
- Analytics overlays are exported as NDJSON (`overlays/clusters.ndjson`, `overlays/centrality.ndjson`) ordered by node id; `overlays/manifest.json` mirrors snapshot id and counts for offline parity. - Analytics overlays are exported as NDJSON (`overlays/clusters.ndjson`, `overlays/centrality.ndjson`) ordered by node id; `overlays/manifest.json` mirrors snapshot id and counts for offline parity.
## 6) Observability ## 6) Observability
- Metrics: ingestion lag (`graph_ingest_lag_seconds`), node/edge counts, query latency per saved query, overlay generation duration. - Metrics: ingestion lag (`graph_ingest_lag_seconds`), node/edge counts, query latency per saved query, overlay generation duration.
- New analytics metrics: `graph_analytics_runs_total`, `graph_analytics_failures_total`, `graph_analytics_clusters_total`, `graph_analytics_centrality_total`, plus change-stream/backfill counters (`graph_changes_total`, `graph_backfill_total`, `graph_change_failures_total`, `graph_change_lag_seconds`). - New analytics metrics: `graph_analytics_runs_total`, `graph_analytics_failures_total`, `graph_analytics_clusters_total`, `graph_analytics_centrality_total`, plus change-stream/backfill counters (`graph_changes_total`, `graph_backfill_total`, `graph_change_failures_total`, `graph_change_lag_seconds`).
- Logs: structured events for ETL stages and query execution (with trace IDs). - Logs: structured events for ETL stages and query execution (with trace IDs).
- Traces: ETL pipeline spans, query engine spans. - Traces: ETL pipeline spans, query engine spans.
## 7) Rollout notes ## 7) Rollout notes
- Phase 1: ingest SBOM + advisories, deliver impact queries. - Phase 1: ingest SBOM + advisories, deliver impact queries.
- Phase 2: add VEX overlays, policy overlays, diff tooling. - Phase 2: add VEX overlays, policy overlays, diff tooling.
- Phase 3: expose runtime/Zastava edges and AI-assisted recommendations (future). - Phase 3: expose runtime/Zastava edges and AI-assisted recommendations (future).
### Local testing note ### Local testing note
Set `STELLAOPS_TEST_POSTGRES_CONNECTION` to a reachable PostgreSQL instance before running `tests/Graph/StellaOps.Graph.Indexer.Tests`. The test harness falls back to `Host=127.0.0.1;Port=5432;Database=stellaops_test`, then Testcontainers for PostgreSQL, but the CI workflow requires the environment variable to be present to ensure upsert coverage runs against a managed database. Use `STELLAOPS_GRAPH_SNAPSHOT_DIR` (or the `AddSbomIngestPipeline` options callback) to control where graph snapshot artefacts land during local runs. Set `STELLAOPS_TEST_POSTGRES_CONNECTION` to a reachable PostgreSQL instance before running `tests/Graph/StellaOps.Graph.Indexer.Tests`. The test harness falls back to `Host=127.0.0.1;Port=5432;Database=stellaops_test`, then Testcontainers for PostgreSQL, but the CI workflow requires the environment variable to be present to ensure upsert coverage runs against a managed database. Use `STELLAOPS_GRAPH_SNAPSHOT_DIR` (or the `AddSbomIngestPipeline` options callback) to control where graph snapshot artefacts land during local runs.
Refer to the module README and implementation plan for immediate context, and update this document once component boundaries and data flows are finalised. Refer to the module README and implementation plan for immediate context, and update this document once component boundaries and data flows are finalised.

View File

@@ -1,16 +1,16 @@
# Advisory AI Assistant Parameters # Advisory AI Assistant Parameters
_Primary audience: platform operators & policy authors • Updated: 2026-01-13_ _Primary audience: platform operators & policy authors Updated: 2026-01-13_
This note centralises the tunable knobs that control Advisory AI’s planner, retrieval stack, inference clients, and guardrails. All options live under the `AdvisoryAI` configuration section and can be set via `appsettings.*` files or environment variables using ASP.NET Core’s double-underscore convention (`ADVISORYAI__Inference__Mode`, etc.). Chat quotas and tool allowlists can also be overridden per tenant/user via the chat settings endpoints; appsettings/env values are defaults. This note centralises the tunable knobs that control Advisory AIs planner, retrieval stack, inference clients, and guardrails. All options live under the `AdvisoryAI` configuration section and can be set via `appsettings.*` files or environment variables using ASP.NET Cores double-underscore convention (`ADVISORYAI__Inference__Mode`, etc.). Chat quotas and tool allowlists can also be overridden per tenant/user via the chat settings endpoints; appsettings/env values are defaults.
**Policy/version pin** — For Sprint 0111, use the policy bundle hash shipped on 2025-11-19 (same drop as `CLI-VULN-29-001` / `CLI-VEX-30-001`). Set `AdvisoryAI:PolicyVersion` or `ADVISORYAI__POLICYVERSION=2025.11.19` in deployments; include the hash in DSSE metadata for Offline Kits. **Policy/version pin** For Sprint 0111, use the policy bundle hash shipped on 2025-11-19 (same drop as `CLI-VULN-29-001` / `CLI-VEX-30-001`). Set `AdvisoryAI:PolicyVersion` or `ADVISORYAI__POLICYVERSION=2025.11.19` in deployments; include the hash in DSSE metadata for Offline Kits.
| Area | Key(s) | Environment variable | Default | Notes | | Area | Key(s) | Environment variable | Default | Notes |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| Inference mode | `AdvisoryAI:Inference:Mode` | `ADVISORYAI__INFERENCE__MODE` | `Local` | `Local` runs the deterministic pipeline only; `Remote` posts sanitized prompts to `Remote.BaseAddress`. | | Inference mode | `AdvisoryAI:Inference:Mode` | `ADVISORYAI__INFERENCE__MODE` | `Local` | `Local` runs the deterministic pipeline only; `Remote` posts sanitized prompts to `Remote.BaseAddress`. |
| Remote base URI | `AdvisoryAI:Inference:Remote:BaseAddress` | `ADVISORYAI__INFERENCE__REMOTE__BASEADDRESS` | — | Required when `Mode=Remote`. HTTPS strongly recommended. | | Remote base URI | `AdvisoryAI:Inference:Remote:BaseAddress` | `ADVISORYAI__INFERENCE__REMOTE__BASEADDRESS` | | Required when `Mode=Remote`. HTTPS strongly recommended. |
| Remote API key | `AdvisoryAI:Inference:Remote:ApiKey` | `ADVISORYAI__INFERENCE__REMOTE__APIKEY` | — | Injected as `Authorization: Bearer <key>` when present. | | Remote API key | `AdvisoryAI:Inference:Remote:ApiKey` | `ADVISORYAI__INFERENCE__REMOTE__APIKEY` | | Injected as `Authorization: Bearer <key>` when present. |
| Remote timeout | `AdvisoryAI:Inference:Remote:TimeoutSeconds` | `ADVISORYAI__INFERENCE__REMOTE__TIMEOUTSECONDS` | `30` | Failing requests fall back to the sanitized prompt with `inference.fallback_reason=remote_timeout`. | | Remote timeout | `AdvisoryAI:Inference:Remote:TimeoutSeconds` | `ADVISORYAI__INFERENCE__REMOTE__TIMEOUTSECONDS` | `30` | Failing requests fall back to the sanitized prompt with `inference.fallback_reason=remote_timeout`. |
| Guardrail prompt cap | `AdvisoryAI:Guardrails:MaxPromptLength` | `ADVISORYAI__GUARDRAILS__MAXPROMPTLENGTH` | `16000` | Prompts longer than the cap are blocked with `prompt_too_long`. | | Guardrail prompt cap | `AdvisoryAI:Guardrails:MaxPromptLength` | `ADVISORYAI__GUARDRAILS__MAXPROMPTLENGTH` | `16000` | Prompts longer than the cap are blocked with `prompt_too_long`. |
| Guardrail citations | `AdvisoryAI:Guardrails:RequireCitations` | `ADVISORYAI__GUARDRAILS__REQUIRECITATIONS` | `true` | When `true`, at least one citation must accompany every prompt. | | Guardrail citations | `AdvisoryAI:Guardrails:RequireCitations` | `ADVISORYAI__GUARDRAILS__REQUIRECITATIONS` | `true` | When `true`, at least one citation must accompany every prompt. |
@@ -39,12 +39,12 @@ This note centralises the tunable knobs that control Advisory AI’s planne
--- ---
## 1. Inference knobs & “temperature” ## 1. Inference knobs & temperature
Advisory AI supports two inference modes: Advisory AI supports two inference modes:
- **Local (default)** – The orchestrator emits deterministic prompts and the worker returns the sanitized prompt verbatim. This mode is offline-friendly and does **not** call any external LLMs. There is no stochastic “temperature” here—the pipeline is purely rule-based. - **Local (default)** The orchestrator emits deterministic prompts and the worker returns the sanitized prompt verbatim. This mode is offline-friendly and does **not** call any external LLMs. There is no stochastic temperature herethe pipeline is purely rule-based.
- **Remote** – Sanitized prompts, citations, and metadata are POSTed to `Remote.BaseAddress + Remote.Endpoint` (default `/v1/inference`). Remote providers control sampling temperature on their side. StellaOps treats remote responses deterministically: we record the provider’s `modelId`, token usage, and any metadata they return. If your remote tier exposes a temperature knob, set it there; Advisory AI simply forwards the prompt. - **Remote** Sanitized prompts, citations, and metadata are POSTed to `Remote.BaseAddress + Remote.Endpoint` (default `/v1/inference`). Remote providers control sampling temperature on their side. StellaOps treats remote responses deterministically: we record the providers `modelId`, token usage, and any metadata they return. If your remote tier exposes a temperature knob, set it there; Advisory AI simply forwards the prompt.
### Remote inference quick sample ### Remote inference quick sample
@@ -68,14 +68,14 @@ Advisory AI supports two inference modes:
| Setting | Default | Explanation | | Setting | Default | Explanation |
| --- | --- | --- | | --- | --- | --- |
| `MaxPromptLength` | 16000 chars | Upper bound enforced after redaction. Increase cautiously—remote providers typically cap prompts at 32k tokens. | | `MaxPromptLength` | 16000 chars | Upper bound enforced after redaction. Increase cautiouslyremote providers typically cap prompts at 32k tokens. |
| `RequireCitations` | `true` | Forces each prompt to include at least one citation. Disable only when testing synthetic prompts. | | `RequireCitations` | `true` | Forces each prompt to include at least one citation. Disable only when testing synthetic prompts. |
| `BlockedPhrases[]` | `ignore previous instructions`, `disregard earlier instructions`, `you are now the system`, `override the system prompt`, `please jailbreak` | Inline list merged with the optional file. Comparisons are case-insensitive. | | `BlockedPhrases[]` | `ignore previous instructions`, `disregard earlier instructions`, `you are now the system`, `override the system prompt`, `please jailbreak` | Inline list merged with the optional file. Comparisons are case-insensitive. |
| `BlockedPhraseFile` | — | Points to a newline-delimited list. Relative paths resolve against the content root (`AdvisoryAI.Hosting` sticks to AppContext base). | | `BlockedPhraseFile` | | Points to a newline-delimited list. Relative paths resolve against the content root (`AdvisoryAI.Hosting` sticks to AppContext base). |
| `EntropyThreshold` | `3.5` | Shannon entropy threshold for high-risk token redaction. Set to `0` to disable entropy checks. | | `EntropyThreshold` | `3.5` | Shannon entropy threshold for high-risk token redaction. Set to `0` to disable entropy checks. |
| `EntropyMinLength` | `20` | Minimum token length evaluated by the entropy scrubber. | | `EntropyMinLength` | `20` | Minimum token length evaluated by the entropy scrubber. |
| `AllowlistPatterns` | Defaults (sha256/sha1/sha384/sha512) | Regex patterns that bypass entropy redaction for known-safe identifiers. | | `AllowlistPatterns` | Defaults (sha256/sha1/sha384/sha512) | Regex patterns that bypass entropy redaction for known-safe identifiers. |
| `AllowlistFile` | — | Optional allowlist file (JSON array or newline-delimited). Paths resolve against the content root. | | `AllowlistFile` | | Optional allowlist file (JSON array or newline-delimited). Paths resolve against the content root. |
Violations surface in the response metadata (`guardrail.violations[*]`) and increment `advisory_ai_guardrail_blocks_total`. Console consumes the same payload for its ribbon state. Violations surface in the response metadata (`guardrail.violations[*]`) and increment `advisory_ai_guardrail_blocks_total`. Console consumes the same payload for its ribbon state.
@@ -95,9 +95,9 @@ Each task type (Summary, Conflict, Remediation) inherits the defaults below. Ove
| Task | `StructuredMaxChunks` | `VectorTopK` | `VectorQueries` (default) | `SbomMaxTimelineEntries` | `SbomMaxDependencyPaths` | `IncludeBlastRadius` | | Task | `StructuredMaxChunks` | `VectorTopK` | `VectorQueries` (default) | `SbomMaxTimelineEntries` | `SbomMaxDependencyPaths` | `IncludeBlastRadius` |
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| Summary | 25 | 5 | `Summarize key facts`, `What is impacted?` | 10 | 20 | ✔ | | Summary | 25 | 5 | `Summarize key facts`, `What is impacted?` | 10 | 20 | |
| Conflict | 30 | 6 | `Highlight conflicting statements`, `Where do sources disagree?` | 8 | 15 | ✖ | | Conflict | 30 | 6 | `Highlight conflicting statements`, `Where do sources disagree?` | 8 | 15 | |
| Remediation | 35 | 6 | `Provide remediation steps`, `Outline mitigations and fixes` | 12 | 25 | ✔ | | Remediation | 35 | 6 | `Provide remediation steps`, `Outline mitigations and fixes` | 12 | 25 | |
These knobs act as weighting levers: lower `VectorTopK` emphasises deterministic evidence; higher values favor breadth. `StructuredMaxChunks` bounds how many CSAF/OSV/VEX chunks reach the prompt, keeping token budgets predictable. These knobs act as weighting levers: lower `VectorTopK` emphasises deterministic evidence; higher values favor breadth. `StructuredMaxChunks` bounds how many CSAF/OSV/VEX chunks reach the prompt, keeping token budgets predictable.
@@ -107,17 +107,17 @@ These knobs act as weighting levers: lower `VectorTopK` emphasises deterministic
| Task | Prompt tokens | Completion tokens | | Task | Prompt tokens | Completion tokens |
| --- | --- | --- | | --- | --- | --- |
| Summary | 2 048 | 512 | | Summary | 2048 | 512 |
| Conflict | 2 048 | 512 | | Conflict | 2048 | 512 |
| Remediation | 2 048 | 640 | | Remediation | 2048 | 640 |
Overwrite via `AdvisoryAI:Tasks:Summary:Budget:PromptTokens`, etc. The worker records actual consumption in the response metadata (`inference.prompt_tokens`, `inference.completion_tokens`). Overwrite via `AdvisoryAI:Tasks:Summary:Budget:PromptTokens`, etc. The worker records actual consumption in the response metadata (`inference.prompt_tokens`, `inference.completion_tokens`).
## 5. Cache TTLs & queue directories ## 5. Cache TTLs & queue directories
- **Plan cache TTLs** – In-memory and file-system caches honour `AdvisoryAI:PlanCache:DefaultTimeToLive` (default 10 minutes) and `CleanupInterval` (default 5 minutes). Shorten the TTL to reduce stale plans or increase it to favour offline reuse. Both values accept ISO 8601 or `hh:mm:ss` time spans. - **Plan cache TTLs** In-memory and file-system caches honour `AdvisoryAI:PlanCache:DefaultTimeToLive` (default 10 minutes) and `CleanupInterval` (default 5 minutes). Shorten the TTL to reduce stale plans or increase it to favour offline reuse. Both values accept ISO 8601 or `hh:mm:ss` time spans.
- **Queue & storage paths** – `AdvisoryAI:Queue:DirectoryPath`, `AdvisoryAI:Storage:PlanCacheDirectory`, and `AdvisoryAI:Storage:OutputDirectory` default to `data/advisory-ai/{queue,plans,outputs}` under the content root; override these when mounting RWX volumes in sovereign clusters. - **Queue & storage paths** `AdvisoryAI:Queue:DirectoryPath`, `AdvisoryAI:Storage:PlanCacheDirectory`, and `AdvisoryAI:Storage:OutputDirectory` default to `data/advisory-ai/{queue,plans,outputs}` under the content root; override these when mounting RWX volumes in sovereign clusters.
- **Output TTLs** – Output artefacts inherit the host file-system retention policies. Combine `DefaultTimeToLive` with a cron or systemd timer to prune `outputs/` periodically when operating in remote-inference-heavy environments. - **Output TTLs** Output artefacts inherit the host file-system retention policies. Combine `DefaultTimeToLive` with a cron or systemd timer to prune `outputs/` periodically when operating in remote-inference-heavy environments.
### Example: raised TTL & custom queue path ### Example: raised TTL & custom queue path
@@ -138,6 +138,6 @@ Overwrite via `AdvisoryAI:Tasks:Summary:Budget:PromptTokens`, etc. The worker re
## 6. Operational notes ## 6. Operational notes
- Updating **guardrail phrases** triggers only on host reload. When distributing blocked-phrase files via Offline Kits, keep filenames stable and version them through Git so QA can diff changes. - Updating **guardrail phrases** triggers only on host reload. When distributing blocked-phrase files via Offline Kits, keep filenames stable and version them through Git so QA can diff changes.
- **Temperature / sampling** remains a remote-provider concern. StellaOps records the provider’s `modelId` and exposes fallback metadata so policy authors can audit when sanitized prompts were returned instead of model output. - **Temperature / sampling** remains a remote-provider concern. StellaOps records the providers `modelId` and exposes fallback metadata so policy authors can audit when sanitized prompts were returned instead of model output.
- Always track changes in `docs/implplan/SPRINT_0111_0001_0001_advisoryai.md` (task `DOCS-AIAI-31-006`) when promoting this document so the guild can trace which parameters were added per sprint. - Always track changes in `docs/implplan/SPRINT_0111_0001_0001_advisoryai.md` (task `DOCS-AIAI-31-006`) when promoting this document so the guild can trace which parameters were added per sprint.

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +1,143 @@
# S00 Nav Rendering Policy # S00 Nav Rendering Policy
Status: Frozen Status: Frozen
Date: 2026-02-18 Date: 2026-03-29
Working directory: `docs/modules/ui/v2-rewire` Working directory: `docs/modules/ui/v2-rewire`
Sprint: `20260218_005`, task `R0-03` Sprint: `20260218_005`, task `R0-03` (updated 2026-03-29 for 6-group restructure)
## Policy statement ## Policy statement
Release Control-owned capabilities may be rendered as direct shortcuts in the sidebar if and only if: Capabilities may be rendered as direct shortcuts in the sidebar if and only if:
1. Ownership is labeled as **Release Control** in breadcrumbs and page headers. 1. Ownership is labeled with the correct **group name** in breadcrumbs and page headers.
2. The canonical routes for those capabilities live under `/release-control/*`. 2. The canonical routes for those capabilities live under the group's route prefix.
3. The sidebar shortcut links to the canonical route, not an alias. 3. The sidebar shortcut links to the canonical route, not an alias.
This policy prevents mixed rendering where the same screen appears to be owned by two domains. This policy prevents mixed rendering where the same screen appears to be owned by two domains.
## Canonical sidebar groups (6 groups)
The sidebar is organized into exactly 6 menu groups identified by `menuGroupId`:
| # | menuGroupId | Rendered label | Purpose |
|---|-------------|----------------|---------|
| 1 | `home` | *(none)* | Dashboard entry point |
| 2 | `release-control` | Release Control | Deployments, Releases, Environments, Readiness |
| 3 | `security` | Security | Vulnerabilities, Posture, Scan, VEX & Exceptions, Risk & Governance (incl. Simulation, Audit) |
| 4 | `evidence` | Evidence | Evidence Overview, Decision Capsules, Audit Log, Export Center |
| 5 | `operations` | Operations | Operations Hub, Policy Packs, Scheduled Jobs, Feeds & Airgap, Agent Fleet, Signals, Scripts, Diagnostics |
| 6 | `setup-admin` | Settings | Integrations, Identity & Access, Certificates & Trust, Theme & Branding, User Preferences |
### Policy dissolution
The former `policy` group was dissolved in the 7-to-6 restructure. Policy is a toolset, not a persona -- its items were distributed by audience:
- **Security** absorbed: VEX & Exceptions, Risk & Governance (including Simulation and Policy Audit). These are consumed by security practitioners during triage and risk assessment.
- **Operations** absorbed: Policy Packs. Pack authoring and management is an operational/admin workflow.
There is no standalone "Policy" group in the sidebar. The `/ops/policy/*` route prefix is retained for backward compatibility but items are surfaced under their new parent groups.
### New items surfaced (2026-03-29)
- **Readiness** (`/releases/readiness`) -- added to Release Control. Surfaces gate status and release readiness checks before promotion.
- **Findings Explorer** (`/security/findings`) -- added as a child of Security Posture. Provides a filterable view of all security findings across the fleet.
### Items removed from direct navigation
The following items are no longer direct sidebar entries but remain routable for deep links and contextual navigation:
- Runtime Drift, Notifications, Watchlist -- accessible from Operations Hub landing page
- Replay & Verify, Bundles, Trust -- accessible from Evidence Overview and Decision Capsules detail
## Allowed rendering model ## Allowed rendering model
### Desktop (expanded sidebar) ### Desktop (expanded sidebar)
``` ```
Dashboard [Home]
Dashboard
Release Control Release Control
├── Releases [shortcut direct nav allowed] Deployments [badge: failed runs + pending approvals]
├── Approvals [shortcut direct nav allowed] Releases [badge: blocked gates]
├── Bundles [nested only — no direct shortcut] Environments
├── Deployments [nested only — no direct shortcut] Readiness
└── Regions & Environments [nested only — no direct shortcut] Security
Security & Risk Vulnerabilities [badge: critical findings]
Evidence & Audit Security Posture [sparkline: security trend]
Integrations Supply-Chain Data
Platform Ops Findings Explorer
Administration Reachability
Unknowns
Scan Image
VEX & Exceptions
Risk & Governance
Simulation
Policy Audit
Evidence
Evidence Overview
Decision Capsules
Audit Log
Export Center
Operations
Operations Hub [sparkline: platform trend]
Policy Packs
Scheduled Jobs
Feeds & Airgap
Agent Fleet
Signals
Scripts
Diagnostics
Settings
Integrations
Identity & Access
Certificates & Trust
Theme & Branding
User Preferences
``` ```
`Releases` and `Approvals` may appear as direct children under `Release Control` in the sidebar ### Desktop (collapsed sidebar -- icons only)
(rather than requiring expand → click).
`Bundles`, `Deployments`, and `Regions & Environments` remain nested and require expand.
### Desktop (collapsed sidebar — icons only) - Show icon for each group root only.
- Tooltip on hover shows the group label.
- Show icon for Release Control root only. - Click navigates to the first item in the group or last active child.
- Tooltip on hover shows "Release Control". - No separate child icons in collapsed mode.
- Click navigates to Release Control overview or last active child.
- No separate Releases / Approvals icons in collapsed mode.
### Mobile (navigation drawer) ### Mobile (navigation drawer)
- All root domains appear as top-level items in the drawer. - All 6 groups appear as top-level items in the drawer.
- Release Control expands in-place to show child nav items. - Groups expand in-place to show child nav items.
- `Releases` and `Approvals` may appear as drawer children with Release Control as visible parent. - No capabilities may appear as top-level drawer items separate from their group.
- No Release Control capabilities may appear as top-level drawer items separate from the Release Control group.
## Breadcrumb rules ## Breadcrumb rules
Canonical format: `Root Domain > Capability > [Sub-page]` Canonical format: `Group > Capability > [Sub-page]`
| Scenario | Breadcrumb | Notes | | Scenario | Breadcrumb | Notes |
| --- | --- | --- | | --- | --- | --- |
| Releases list | `Release Control > Releases` | No shortcut bypasses ownership label | | Deployments list | `Release Control > Deployments` | |
| Release detail | `Release Control > Releases > RCB-1234` | ID or name appended | | Release detail | `Release Control > Releases > RCB-1234` | ID or name appended |
| Approvals queue | `Release Control > Approvals` | | | Readiness | `Release Control > Readiness` | New item |
| Approval detail | `Release Control > Approvals > APR-5678` | | | Vulnerabilities | `Security > Vulnerabilities` | |
| Bundle catalog | `Release Control > Bundles` | | | Findings Explorer | `Security > Security Posture > Findings Explorer` | Nested under posture |
| Bundle detail | `Release Control > Bundles > my-bundle` | | | VEX & Exceptions | `Security > VEX & Exceptions` | Absorbed from Policy |
| Bundle version detail | `Release Control > Bundles > my-bundle > v1.3.0` | | | Risk & Governance | `Security > Risk & Governance` | Absorbed from Policy |
| Deployments | `Release Control > Deployments` | | | Policy Simulation | `Security > Risk & Governance > Simulation` | Nested child |
| Environments list | `Release Control > Regions & Environments` | | | Evidence Overview | `Evidence > Evidence Overview` | |
| Environment detail | `Release Control > Regions & Environments > staging-eu` | | | Decision Capsules | `Evidence > Decision Capsules` | |
| Audit Log | `Evidence > Audit Log` | |
| Operations Hub | `Operations > Operations Hub` | |
| Policy Packs | `Operations > Policy Packs` | Absorbed from Policy |
| Integrations | `Settings > Integrations` | |
| Certificates & Trust | `Settings > Certificates & Trust` | Renamed from "Certificates" |
### Concrete counter-examples (forbidden) ### Concrete counter-examples (forbidden)
| Forbidden breadcrumb | Reason | | Forbidden breadcrumb | Reason |
| --- | --- | | --- | --- |
| `Approvals > APR-5678` | Missing Release Control ownership prefix | | `Policy > VEX & Exceptions` | Policy group no longer exists; VEX is under Security |
| `Releases` (no parent) | Same — no domain context | | `Policy > Packs` | Policy group dissolved; Packs is under Operations |
| `Settings > Policy Governance` | Policy Governance owner is Administration, not Settings | | `Approvals > APR-5678` | Missing group ownership prefix |
| `Evidence & Audit > Trust & Signing` | Trust & Signing owner is Administration; Evidence may only show a consumer link | | `Releases` (no parent) | No domain context |
| `Evidence > Trust` | Trust item removed from Evidence nav |
## Legacy label transition behavior ## Legacy label transition behavior
@@ -83,7 +145,7 @@ Where users know a surface by an old label, show a compact transition label duri
Rules: Rules:
- Transition labels appear only in page headers and sidebar items, not in breadcrumbs. - Transition labels appear only in page headers and sidebar items, not in breadcrumbs.
- Format: canonical label is primary; old label appears parenthetically e.g., `Policy Governance (formerly Policy Studio)`. - Format: canonical label is primary; old label appears parenthetically -- e.g., `Certificates & Trust (formerly Certificates)`.
- Transition labels are removed at sprint 016 cutover unless traffic evidence requires extension. - Transition labels are removed at sprint 016 cutover unless traffic evidence requires extension.
- Canonical labels are always primary; old labels never replace canonical ones. - Canonical labels are always primary; old labels never replace canonical ones.
@@ -91,26 +153,26 @@ Planned transition labels:
| Canonical label | Transition label (migration window only) | Remove at | | Canonical label | Transition label (migration window only) | Remove at |
| --- | --- | --- | | --- | --- | --- |
| `Security & Risk` | `Security & Risk (formerly Security)` | Sprint 016 | | `Security` | `Security (formerly Security & Risk)` | Sprint 016 |
| `Platform Ops` | `Platform Ops (formerly Operations)` | Sprint 016 | | `Operations` | `Operations (formerly Platform Ops)` | Sprint 016 |
| `Evidence & Audit` | `Evidence & Audit (formerly Evidence)` | Sprint 016 | | `Evidence` | `Evidence (formerly Evidence & Audit)` | Sprint 016 |
| `Policy Governance` | `Policy Governance (formerly Policy Studio / Policy)` | Sprint 016 | | `Certificates & Trust` | `Certificates & Trust (formerly Certificates)` | Sprint 016 |
## Explicit do-not list ## Explicit do-not list
The following rendering patterns are forbidden in any sprint implementation: The following rendering patterns are forbidden in any sprint implementation:
1. **Do not** place Release Control capability screens (`Releases`, `Approvals`, `Bundles`, `Deployments`, `Environments`) as root-level sidebar items independent from the `Release Control` group. 1. **Do not** place any capability as a root-level sidebar item independent from its canonical group.
2. **Do not** display a breadcrumb that omits the canonical root domain prefix. 2. **Do not** display a breadcrumb that omits the canonical group prefix.
3. **Do not** show different ownership labels on desktop vs. mobile for the same screen. 3. **Do not** show different ownership labels on desktop vs. mobile for the same screen.
4. **Do not** use legacy root-level nav paths (e.g., `/approvals`, `/releases`) as the canonical nav target they must redirect to `/release-control/*` canonical targets. 4. **Do not** use legacy root-level nav paths (e.g., `/approvals`, `/releases` at root) as the canonical nav target -- they must redirect to canonical targets.
5. **Do not** label `Trust & Signing` as owned by Evidence & Audit or Security in any nav or header. 5. **Do not** reintroduce a `policy` group -- Policy items live under Security and Operations.
6. **Do not** label `Policy Governance` as owned by Release Control in any nav or header. 6. **Do not** label Policy Packs as owned by Security -- Packs authoring is an Operations concern.
7. **Do not** introduce a new root domain that is not in the canonical 7: Dashboard, Release Control, Security & Risk, Evidence & Audit, Integrations, Platform Ops, Administration. 7. **Do not** introduce a new root domain that is not in the canonical 6: Home, Release Control, Security, Evidence, Operations, Settings.
## Route alias requirements for migration ## Route alias requirements for migration
During the alias window, current root-level paths (`/releases`, `/approvals`) must: During the alias window, legacy paths must:
- Resolve to the canonical `/release-control/releases` and `/release-control/approvals` routes. - Resolve to the canonical routes under the new group structure.
- Render the canonical breadcrumb (e.g., `Release Control > Releases`) not an alias-derived breadcrumb. - Render the canonical breadcrumb (e.g., `Security > VEX & Exceptions`) -- not an alias-derived breadcrumb.
- Not appear as primary nav items in the sidebar; the sidebar must link to canonical paths only. - Not appear as primary nav items in the sidebar; the sidebar must link to canonical paths only.

View File

@@ -1,10 +1,10 @@
# Automated Test-Suite Overview # Automated Test-Suite Overview
This document enumerates **every automated check** executed by the Stella Ops This document enumerates **every automated check** executed by the Stella Ops
CI pipeline, from unit level to chaos experiments. It is intended for CI pipeline, from unit level to chaos experiments. It is intended for
contributors who need to extend coverage or diagnose failures. contributors who need to extend coverage or diagnose failures.
> **Build parameters** – values such as `{{ dotnet }}` (runtime) and > **Build parameters** values such as `{{ dotnet }}` (runtime) and
> `{{ angular }}` (UI framework) are injected at build time. > `{{ angular }}` (UI framework) are injected at build time.
--- ---
@@ -13,7 +13,7 @@ contributors who need to extend coverage or diagnose failures.
### Core Principles ### Core Principles
1. **Determinism as Contract**: Scan verdicts must be reproducible. Same inputs → byte-identical outputs. 1. **Determinism as Contract**: Scan verdicts must be reproducible. Same inputs byte-identical outputs.
2. **Offline by Default**: Every test (except explicitly tagged "online") runs without network access. 2. **Offline by Default**: Every test (except explicitly tagged "online") runs without network access.
3. **Evidence-First Validation**: Assertions verify the complete evidence chain, not just pass/fail. 3. **Evidence-First Validation**: Assertions verify the complete evidence chain, not just pass/fail.
4. **Interop is Required**: Compatibility with ecosystem tools (Syft, Grype, Trivy, cosign) blocks releases. 4. **Interop is Required**: Compatibility with ecosystem tools (Syft, Grype, Trivy, cosign) blocks releases.
@@ -78,16 +78,16 @@ the required test types per project model and the module-to-model mapping.
| Metric | Budget | Gate | | Metric | Budget | Gate |
|--------|--------|------| |--------|--------|------|
| API unit coverage | ≥ 85% lines | PR merge | | API unit coverage | 85% lines | PR merge |
| API response P95 | ≤ 120 ms | nightly alert | | API response P95 | 120 ms | nightly alert |
| Δ-SBOM warm scan P95 (4 vCPU) | ≤ 5 s | nightly alert | | Δ-SBOM warm scan P95 (4 vCPU) | 5 s | nightly alert |
| Lighthouse performance score | ≥ 90 | nightly alert | | Lighthouse performance score | 90 | nightly alert |
| Lighthouse accessibility score | ≥ 95 | nightly alert | | Lighthouse accessibility score | 95 | nightly alert |
| k6 sustained RPS drop | < 5% vs baseline | nightly alert | | k6 sustained RPS drop | < 5% vs baseline | nightly alert |
| **Replay determinism** | 0 byte diff | **Release** | | **Replay determinism** | 0 byte diff | **Release** |
| **Interop findings parity** | ≥ 95% | **Release** | | **Interop findings parity** | 95% | **Release** |
| **Offline E2E** | All pass with no network | **Release** | | **Offline E2E** | All pass with no network | **Release** |
| **Unknowns budget (prod)** | ≤ configured limit | **Release** | | **Unknowns budget (prod)** | configured limit | **Release** |
| **Router Retry-After compliance** | 100% | Nightly | | **Router Retry-After compliance** | 100% | Nightly |
--- ---
@@ -109,7 +109,7 @@ dotnet test --filter "Category=Interop"
The script spins up PostgreSQL/Valkey via Testcontainers and requires: The script spins up PostgreSQL/Valkey via Testcontainers and requires:
* Docker ≥ 25 * Docker 25
* Node 20 (for Jest/Playwright) * Node 20 (for Jest/Playwright)
### PostgreSQL Testcontainers ### PostgreSQL Testcontainers
@@ -158,7 +158,7 @@ stella replay verify --manifest run-manifest.json
### Evidence Index ### Evidence Index
The **Evidence Index** links verdicts to their supporting evidence chain: The **Evidence Index** links verdicts to their supporting evidence chain:
- Verdict → SBOM digests → Attestation IDs → Tool versions - Verdict SBOM digests Attestation IDs Tool versions
### Golden Corpus ### Golden Corpus
@@ -191,7 +191,7 @@ public class OfflineTests : NetworkIsolatedTestBase
--- ---
## Concelier OSV↔GHSA Parity Fixtures ## Concelier OSVGHSA Parity Fixtures
The Concelier connector suite includes a regression test (`OsvGhsaParityRegressionTests`) The Concelier connector suite includes a regression test (`OsvGhsaParityRegressionTests`)
that checks a curated set of GHSA identifiers against OSV responses. The fixture that checks a curated set of GHSA identifiers against OSV responses. The fixture

View File

@@ -1,4 +1,4 @@
# 01. Requirements And Principles # 01. Requirements And Principles
## 1. Product Goal ## 1. Product Goal
@@ -239,7 +239,7 @@ They are related, but they are not the same data structure.
### 5.3 Run To Wait ### 5.3 Run To Wait
The engine should never keep a workflow instance “hot” in memory for correctness. The engine should never keep a workflow instance “hot” in memory for correctness.
Execution should run until: Execution should run until:

View File

@@ -1,15 +1,15 @@
# Tutorial 1: Hello World # Tutorial 1: Hello World
The simplest possible workflow: initialize state from a start request, activate a single human task, and complete the workflow when the task is done. The simplest possible workflow: initialize state from a start request, activate a single human task, and complete the workflow when the task is done.
## Concepts Introduced ## Concepts Introduced
- `IDeclarativeWorkflow<T>` — the contract every workflow implements - `IDeclarativeWorkflow<T>` the contract every workflow implements
- `WorkflowSpec.For<T>()` — the builder entry point - `WorkflowSpec.For<T>()` the builder entry point
- `.InitializeState()` — transforms the start request into workflow state - `.InitializeState()` transforms the start request into workflow state
- `.StartWith(task)` — sets the first task to activate - `.StartWith(task)` sets the first task to activate
- `WorkflowHumanTask.For<T>()` — defines a human task - `WorkflowHumanTask.For<T>()` defines a human task
- `.OnComplete(flow => flow.Complete())` — terminal step - `.OnComplete(flow => flow.Complete())` terminal step
## What Happens at Runtime ## What Happens at Runtime
@@ -17,7 +17,7 @@ The simplest possible workflow: initialize state from a start request, activate
2. State initializes to `{ "customerName": "John" }` 2. State initializes to `{ "customerName": "John" }`
3. Task "Greet Customer" is created with status "Pending" 3. Task "Greet Customer" is created with status "Pending"
4. A user assigns the task to themselves, then completes it 4. A user assigns the task to themselves, then completes it
5. `OnComplete` executes `.Complete()` — the workflow finishes 5. `OnComplete` executes `.Complete()` the workflow finishes
## Variants ## Variants
@@ -26,5 +26,5 @@ The simplest possible workflow: initialize state from a start request, activate
## Next ## Next
[Tutorial 2: Service Tasks](../02-service-tasks/) — call external services before or after human tasks. [Tutorial 2: Service Tasks](../02-service-tasks/) call external services before or after human tasks.

View File

@@ -1,15 +1,15 @@
# Tutorial 2: Service Tasks # Tutorial 2: Service Tasks
Call external services (microservices, HTTP APIs, GraphQL, RabbitMQ) from within a workflow. Handle failures and timeouts gracefully. Call external services (microservices, HTTP APIs, GraphQL, RabbitMQ) from within a workflow. Handle failures and timeouts gracefully.
## Concepts Introduced ## Concepts Introduced
- `.Call()` — invoke a transport with payload and optional response capture - `.Call()` invoke a transport with payload and optional response capture
- Address types — `LegacyRabbit`, `Microservice`, `Http`, `Graphql`, `Rabbit` - Address types `LegacyRabbit`, `Microservice`, `Http`, `Graphql`, `Rabbit`
- `resultKey` — store the service response in workflow state - `resultKey` store the service response in workflow state
- `whenFailure` / `whenTimeout` — recovery branches - `whenFailure` / `whenTimeout` recovery branches
- `WorkflowHandledBranchAction.Complete` — shorthand for "complete on error" - `WorkflowHandledBranchAction.Complete` shorthand for "complete on error"
- `timeoutSeconds` — per-step timeout override (default: 1 hour) - `timeoutSeconds` per-step timeout override (default: 1 hour)
## Key Points ## Key Points
@@ -25,5 +25,5 @@ Call external services (microservices, HTTP APIs, GraphQL, RabbitMQ) from within
## Next ## Next
[Tutorial 3: Decisions](../03-decisions/) — branch workflow logic based on conditions. [Tutorial 3: Decisions](../03-decisions/) branch workflow logic based on conditions.

View File

@@ -1,13 +1,13 @@
# Tutorial 3: Decisions # Tutorial 3: Decisions
Branch workflow logic based on conditions — state values, payload answers, or complex expressions. Branch workflow logic based on conditions state values, payload answers, or complex expressions.
## Concepts Introduced ## Concepts Introduced
- `.WhenExpression()` — branch on any boolean expression - `.WhenExpression()` branch on any boolean expression
- `.WhenStateFlag()` — shorthand for checking a boolean state value - `.WhenStateFlag()` shorthand for checking a boolean state value
- `.WhenPayloadEquals()` — shorthand for checking a task completion payload value - `.WhenPayloadEquals()` shorthand for checking a task completion payload value
- Nested decisions — decisions inside decisions for complex routing - Nested decisions decisions inside decisions for complex routing
## Decision Types ## Decision Types
@@ -24,5 +24,5 @@ Branch workflow logic based on conditions — state values, payload answers
## Next ## Next
[Tutorial 4: Human Tasks](../04-human-tasks/) — approve/reject patterns with OnComplete flows. [Tutorial 4: Human Tasks](../04-human-tasks/) approve/reject patterns with OnComplete flows.

View File

@@ -1,26 +1,26 @@
# Tutorial 4: Human Tasks with OnComplete Flows # Tutorial 4: Human Tasks with OnComplete Flows
The approve/reject pattern — the most common human task flow in insurance workflows. The approve/reject pattern the most common human task flow in insurance workflows.
## Concepts Introduced ## Concepts Introduced
- `WorkflowHumanTask.For<T>()` — define a task with name, type, route, and roles - `WorkflowHumanTask.For<T>()` define a task with name, type, route, and roles
- `.WithPayload()` — data sent to the UI when the task is displayed - `.WithPayload()` data sent to the UI when the task is displayed
- `.WithTimeout(seconds)` — optional deadline for the task - `.WithTimeout(seconds)` optional deadline for the task
- `.WithRoles()` — restrict which roles can interact with this task - `.WithRoles()` restrict which roles can interact with this task
- `.OnComplete(flow => ...)` — sequence executed after user completes the task - `.OnComplete(flow => ...)` sequence executed after user completes the task
- `.ActivateTask()` — pause workflow and wait for user action - `.ActivateTask()` pause workflow and wait for user action
- `.AddTask()` — register a task in the workflow spec (separate from activation) - `.AddTask()` register a task in the workflow spec (separate from activation)
- Re-activation — send the user back to the same task on validation failure - Re-activation send the user back to the same task on validation failure
## Approve/Reject Pattern ## Approve/Reject Pattern
1. Workflow starts, runs some service tasks 1. Workflow starts, runs some service tasks
2. `.ActivateTask("Approve")` — workflow pauses 2. `.ActivateTask("Approve")` workflow pauses
3. User sees the task in their inbox, assigns it, submits an answer 3. User sees the task in their inbox, assigns it, submits an answer
4. `.OnComplete` checks `payload.answer`: 4. `.OnComplete` checks `payload.answer`:
- `"approve"` — run confirmation operations, convert to policy - `"approve"` run confirmation operations, convert to policy
- `"reject"` — cancel the application - `"reject"` cancel the application
5. If operations fail, re-activate the same task for correction 5. If operations fail, re-activate the same task for correction
## Variants ## Variants
@@ -30,5 +30,5 @@ The approve/reject pattern — the most common human task flow in insurance
## Next ## Next
[Tutorial 5: Sub-Workflows](../05-sub-workflows/) — inline vs fire-and-forget child workflows. [Tutorial 5: Sub-Workflows](../05-sub-workflows/) inline vs fire-and-forget child workflows.

View File

@@ -1,14 +1,14 @@
# Tutorial 5: Sub-Workflows & Continuations # Tutorial 5: Sub-Workflows & Continuations
Compose workflows by invoking child workflows — either inline (SubWorkflow) or fire-and-forget (ContinueWith). Compose workflows by invoking child workflows either inline (SubWorkflow) or fire-and-forget (ContinueWith).
## SubWorkflow vs ContinueWith ## SubWorkflow vs ContinueWith
| Feature | `.SubWorkflow()` | `.ContinueWith()` | | Feature | `.SubWorkflow()` | `.ContinueWith()` |
|---------|-----------------|-------------------| |---------|-----------------|-------------------|
| Parent waits | Yes — resumes after child completes | No — parent completes immediately | | Parent waits | Yes resumes after child completes | No parent completes immediately |
| State flows back | Yes — child state merges into parent | No — child is independent | | State flows back | Yes child state merges into parent | No child is independent |
| Same instance | Yes — tasks appear under parent instance | No — new workflow instance | | Same instance | Yes tasks appear under parent instance | No new workflow instance |
| Use when | Steps must complete before parent continues | Fire-and-forget, scheduled work | | Use when | Steps must complete before parent continues | Fire-and-forget, scheduled work |
## Variants ## Variants
@@ -18,5 +18,5 @@ Compose workflows by invoking child workflows — either inline (SubWorkflo
## Next ## Next
[Tutorial 6: Advanced Patterns](../06-advanced-patterns/) — Fork, Repeat, Timer, External Signal. [Tutorial 6: Advanced Patterns](../06-advanced-patterns/) Fork, Repeat, Timer, External Signal.

View File

@@ -1,4 +1,4 @@
# Tutorial 6: Advanced Patterns # Tutorial 6: Advanced Patterns
Fork (parallel branches), Repeat (retry loops), Timer (delays), and External Signal (wait for events). Fork (parallel branches), Repeat (retry loops), Timer (delays), and External Signal (wait for events).
@@ -18,5 +18,5 @@ Fork (parallel branches), Repeat (retry loops), Timer (delays), and External Sig
## Next ## Next
[Tutorial 7: Shared Helpers](../07-shared-helpers/) — organizing reusable workflow components. [Tutorial 7: Shared Helpers](../07-shared-helpers/) organizing reusable workflow components.

View File

@@ -1,4 +1,4 @@
# Tutorial 7: Shared Support Helpers # Tutorial 7: Shared Support Helpers
When building many workflows for the same domain (e.g., 50+ policy change workflows), extract reusable components into a support helper class. When building many workflows for the same domain (e.g., 50+ policy change workflows), extract reusable components into a support helper class.
@@ -6,19 +6,19 @@ When building many workflows for the same domain (e.g., 50+ policy change workfl
| Component | Example | | Component | Example |
|-----------|---------| |-----------|---------|
| **Address constants** | `LegacyRabbitAddress`, `HttpAddress` — centralized routing | | **Address constants** | `LegacyRabbitAddress`, `HttpAddress` centralized routing |
| **Workflow references** | `WorkflowReference` — for SubWorkflow/ContinueWith targets | | **Workflow references** | `WorkflowReference` for SubWorkflow/ContinueWith targets |
| **Payload builders** | Static methods returning `WorkflowExpressionDefinition` | | **Payload builders** | Static methods returning `WorkflowExpressionDefinition` |
| **State initializers** | Base state + override pattern | | **State initializers** | Base state + override pattern |
| **Flow extensions** | Extension methods on `WorkflowFlowBuilder<T>` for common sequences | | **Flow extensions** | Extension methods on `WorkflowFlowBuilder<T>` for common sequences |
## C#-Only Tutorial ## C#-Only Tutorial
This tutorial has no JSON equivalent — it covers C# code organization patterns. This tutorial has no JSON equivalent it covers C# code organization patterns.
- [C# Example](csharp/) - [C# Example](csharp/)
## Next ## Next
[Tutorial 8: Expressions](../08-expressions/) — path navigation, functions, and operators. [Tutorial 8: Expressions](../08-expressions/) path navigation, functions, and operators.

View File

@@ -1,4 +1,4 @@
# Tutorial 8: Expressions # Tutorial 8: Expressions
The expression system enables declarative logic that compiles to portable canonical JSON. All expressions are evaluable at runtime without recompilation. The expression system enables declarative logic that compiles to portable canonical JSON. All expressions are evaluable at runtime without recompilation.
@@ -32,5 +32,5 @@ The expression system enables declarative logic that compiles to portable canoni
## Next ## Next
[Tutorial 9: Testing](../09-testing/) — unit test setup with recording transports. [Tutorial 9: Testing](../09-testing/) unit test setup with recording transports.