This commit is contained in:
StellaOps Bot
2025-11-23 23:40:10 +02:00
parent c13355923f
commit 029002ad05
93 changed files with 2160 additions and 285 deletions

View File

@@ -0,0 +1,26 @@
name: Airgap Sealed CI Smoke
on:
push:
branches: [ main ]
paths:
- 'ops/devops/airgap/**'
- '.gitea/workflows/airgap-sealed-ci.yml'
pull_request:
branches: [ main, develop ]
paths:
- 'ops/devops/airgap/**'
- '.gitea/workflows/airgap-sealed-ci.yml'
jobs:
sealed-smoke:
runs-on: ubuntu-22.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install dnslib
run: pip install dnslib
- name: Run sealed-mode smoke
run: sudo ops/devops/airgap/sealed-ci-smoke.sh

View File

@@ -0,0 +1,103 @@
name: AOC Guard CI
on:
push:
branches: [ main ]
paths:
- 'src/Aoc/**'
- 'src/Concelier/**'
- 'src/Authority/**'
- 'src/Excititor/**'
- 'ops/devops/aoc/**'
- '.gitea/workflows/aoc-guard.yml'
pull_request:
branches: [ main, develop ]
paths:
- 'src/Aoc/**'
- 'src/Concelier/**'
- 'src/Authority/**'
- 'src/Excititor/**'
- 'ops/devops/aoc/**'
- '.gitea/workflows/aoc-guard.yml'
jobs:
aoc-guard:
runs-on: ubuntu-22.04
env:
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Restore analyzers
run: dotnet restore src/Aoc/__Analyzers/StellaOps.Aoc.Analyzers/StellaOps.Aoc.Analyzers.csproj
- name: Build analyzers
run: dotnet build src/Aoc/__Analyzers/StellaOps.Aoc.Analyzers/StellaOps.Aoc.Analyzers.csproj -c Release
- name: Run analyzers against ingestion projects
run: |
dotnet build src/Concelier/StellaOps.Concelier.Ingestion/StellaOps.Concelier.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true
dotnet build src/Authority/StellaOps.Authority.Ingestion/StellaOps.Authority.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true
dotnet build src/Excititor/StellaOps.Excititor.Ingestion/StellaOps.Excititor.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true
- name: Run analyzer tests
run: |
mkdir -p $ARTIFACT_DIR
dotnet test src/Aoc/__Tests/StellaOps.Aoc.Analyzers.Tests/StellaOps.Aoc.Analyzers.Tests.csproj -c Release --logger "trx;LogFileName=aoc-tests.trx" --results-directory $ARTIFACT_DIR
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: aoc-guard-artifacts
path: ${{ env.ARTIFACT_DIR }}
aoc-verify:
needs: aoc-guard
runs-on: ubuntu-22.04
if: github.event_name != 'schedule'
env:
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
AOC_VERIFY_SINCE: ${{ github.event.pull_request.base.sha || 'HEAD~1' }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Run AOC verify
env:
STAGING_MONGO_URI: ${{ secrets.STAGING_MONGO_URI || vars.STAGING_MONGO_URI }}
run: |
if [ -z "${STAGING_MONGO_URI:-}" ]; then
echo "::warning::STAGING_MONGO_URI not set; skipping aoc verify"
exit 0
fi
mkdir -p $ARTIFACT_DIR
dotnet run --project src/Aoc/StellaOps.Aoc.Cli -- verify --since "$AOC_VERIFY_SINCE" --mongo "$STAGING_MONGO_URI" --output "$ARTIFACT_DIR/aoc-verify.json" --ndjson "$ARTIFACT_DIR/aoc-verify.ndjson" || VERIFY_EXIT=$?
if [ -n "${VERIFY_EXIT:-}" ] && [ "${VERIFY_EXIT}" -ne 0 ]; then
echo "::error::AOC verify reported violations"; exit ${VERIFY_EXIT}
fi
- name: Upload verify artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: aoc-verify-artifacts
path: ${{ env.ARTIFACT_DIR }}

View File

@@ -0,0 +1,71 @@
name: Export Center CI
on:
push:
branches: [ main ]
paths:
- 'src/ExportCenter/**'
- 'ops/devops/export/**'
- '.gitea/workflows/export-ci.yml'
- 'docs/modules/devops/export-ci-contract.md'
pull_request:
branches: [ main, develop ]
paths:
- 'src/ExportCenter/**'
- 'ops/devops/export/**'
- '.gitea/workflows/export-ci.yml'
- 'docs/modules/devops/export-ci-contract.md'
jobs:
export-ci:
runs-on: ubuntu-22.04
env:
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
MINIO_ACCESS_KEY: exportci
MINIO_SECRET_KEY: exportci123
BUCKET: export-ci
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Restore
run: dotnet restore src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj
- name: Bring up MinIO
run: |
docker compose -f ops/devops/export/minio-compose.yml up -d
sleep 5
MINIO_ENDPOINT=http://localhost:9000 ops/devops/export/seed-minio.sh
- name: Build
run: dotnet build src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj -c Release /p:ContinuousIntegrationBuild=true
- name: Test
run: |
mkdir -p $ARTIFACT_DIR
dotnet test src/ExportCenter/__Tests/StellaOps.ExportCenter.Tests/StellaOps.ExportCenter.Tests.csproj -c Release --logger "trx;LogFileName=export-tests.trx" --results-directory $ARTIFACT_DIR
- name: Trivy/OCI smoke
run: ops/devops/export/trivy-smoke.sh
- name: SBOM
run: syft dir:src/ExportCenter -o spdx-json=$ARTIFACT_DIR/exportcenter.spdx.json
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: export-ci-artifacts
path: ${{ env.ARTIFACT_DIR }}
- name: Teardown MinIO
if: always()
run: docker compose -f ops/devops/export/minio-compose.yml down -v

View File

@@ -0,0 +1,61 @@
name: LNM Backfill CI
on:
workflow_dispatch:
inputs:
mongo_uri:
description: 'Staging Mongo URI (read-only snapshot)'
required: true
type: string
since_commit:
description: 'Git commit to compare (default HEAD)'
required: false
type: string
dry_run:
description: 'Dry run (no writes)'
required: false
default: true
type: boolean
jobs:
lnm-backfill:
runs-on: ubuntu-22.04
env:
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Restore
run: dotnet restore src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj
- name: Run backfill (dry-run supported)
env:
STAGING_MONGO_URI: ${{ inputs.mongo_uri }}
run: |
mkdir -p $ARTIFACT_DIR
EXTRA=()
if [ "${{ inputs.dry_run }}" = "true" ]; then EXTRA+=("--dry-run"); fi
dotnet run --project src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj -- --mode=observations --batch-size=500 --max-conflicts=0 --mongo "$STAGING_MONGO_URI" "${EXTRA[@]}" | tee $ARTIFACT_DIR/backfill-observations.log
dotnet run --project src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj -- --mode=linksets --batch-size=500 --max-conflicts=0 --mongo "$STAGING_MONGO_URI" "${EXTRA[@]}" | tee $ARTIFACT_DIR/backfill-linksets.log
- name: Validate counts
env:
STAGING_MONGO_URI: ${{ inputs.mongo_uri }}
run: |
STAGING_MONGO_URI="$STAGING_MONGO_URI" ops/devops/lnm/backfill-validation.sh
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: lnm-backfill-artifacts
path: ${{ env.ARTIFACT_DIR }}

View File

@@ -0,0 +1,60 @@
name: LNM VEX Backfill
on:
workflow_dispatch:
inputs:
mongo_uri:
description: 'Staging Mongo URI'
required: true
type: string
nats_url:
description: 'NATS URL'
required: true
type: string
redis_url:
description: 'Redis URL'
required: true
type: string
dry_run:
description: 'Dry run (no writes)'
required: false
default: true
type: boolean
jobs:
vex-backfill:
runs-on: ubuntu-22.04
env:
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Restore
run: dotnet restore src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj
- name: Run VEX backfill
env:
STAGING_MONGO_URI: ${{ inputs.mongo_uri }}
NATS_URL: ${{ inputs.nats_url }}
REDIS_URL: ${{ inputs.redis_url }}
run: |
mkdir -p $ARTIFACT_DIR
EXTRA=()
if [ "${{ inputs.dry_run }}" = "true" ]; then EXTRA+=("--dry-run"); fi
dotnet run --project src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj -- --mode=vex --batch-size=500 --max-conflicts=0 --mongo "$STAGING_MONGO_URI" --nats "$NATS_URL" --redis "$REDIS_URL" "${EXTRA[@]}" | tee $ARTIFACT_DIR/vex-backfill.log
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: lnm-vex-backfill-artifacts
path: ${{ env.ARTIFACT_DIR }}

View File

@@ -0,0 +1,14 @@
exportcenter:
image:
repository: registry.stella-ops.org/export-center
tag: latest
objectStorage:
endpoint: http://minio:9000
bucket: export-prod
accessKeySecret: exportcenter-minio
secretKeySecret: exportcenter-minio
signing:
kmsKey: exportcenter-kms
kmsRegion: us-east-1
dsse:
enabled: true

View File

@@ -0,0 +1,15 @@
notify:
image:
repository: registry.stella-ops.org/notify
tag: latest
smtp:
host: smtp.example.com
port: 587
usernameSecret: notify-smtp
passwordSecret: notify-smtp
webhook:
allowedHosts: ["https://hooks.slack.com"]
chat:
webhookSecret: notify-chat
tls:
secretName: notify-tls

View File

@@ -18,15 +18,17 @@
## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- |
| 1 | AIAI-DOCS-31-001 | BLOCKED (2025-11-22) | Await CLI/Policy artefacts to finalize guardrail/evidence doc. | Advisory AI Docs Guild | Author guardrail + evidence docs with upstream references. |
| 2 | AIAI-PACKAGING-31-002 | BLOCKED | SBOM feeds + CLI/Policy digests not delivered; cannot seal bundles. | Advisory AI Release | Package advisory feeds with SBOM pointers + provenance. |
| 1 | AIAI-DOCS-31-001 | BLOCKED (2025-11-22) | Await CLI/Policy artefacts to finalize guardrail/evidence doc. Draft skeleton allowed (non-blocking for dev). | Advisory AI Docs Guild | Author guardrail + evidence docs with upstream references. |
| 2 | AIAI-PACKAGING-31-002 | BLOCKED (DevOps release-only) | SBOM feeds + CLI/Policy digests not delivered; sealing/publishing deferred to DevOps once feeds arrive. Dev can proceed with dry-run bundle layout. | Advisory AI Release | Package advisory feeds with SBOM pointers + provenance. |
| 3 | AIAI-RAG-31-003 | DONE | LNM v1 frozen; RAG payload docs aligned. | Advisory AI + Concelier | Align RAG evidence payloads with LNM schema. |
| 4 | SBOM-AIAI-31-003 | BLOCKED (moved from SPRINT_0110 on 2025-11-23) | CLI-VULN-29-001; CLI-VEX-30-001 | SBOM Service Guild · Advisory AI Guild | Advisory AI hand-off kit for `/v1/sbom/context`; smoke test with tenants. |
| 5 | DOCS-AIAI-31-005/006/008/009 | BLOCKED (moved from SPRINT_0110 on 2025-11-23) | CLI-VULN-29-001; CLI-VEX-30-001; POLICY-ENGINE-31-001; DEVOPS-AIAI-31-001 | Docs Guild | CLI/policy/ops docs; proceed once upstream artefacts land. |
## Action Tracker
| Focus | Action | Owner(s) | Due | Status |
| --- | --- | --- | --- | --- |
| Docs | Draft guardrail evidence doc | Docs Guild | 2025-11-18 | BLOCKED (awaiting CLI/Policy artefacts) |
| Packaging | Define SBOM/policy bundle for Advisory AI | Release Guild | 2025-11-20 | BLOCKED (waiting CLI/Policy artefacts + SBOM feeds) |
| Packaging | Define SBOM/policy bundle for Advisory AI | Release Guild | 2025-11-20 | BLOCKED (release/DevOps only; waiting CLI/Policy artefacts + SBOM feeds) |
## Execution Log
| Date (UTC) | Update | Owner |
@@ -35,6 +37,8 @@
| 2025-11-22 | Began AIAI-DOCS-31-001 and AIAI-RAG-31-003: refreshed guardrail + LNM-aligned RAG docs; awaiting CLI/Policy artefacts before locking outputs. | Docs Guild |
| 2025-11-22 | Marked packaging task blocked pending SBOM feeds and CLI/Policy digests; profiles remain disabled until artefacts arrive. | Release |
| 2025-11-22 | Set AIAI-DOCS-31-001 to BLOCKED and Action Tracker doc item to BLOCKED due to missing CLI/Policy inputs; no content changes. | Implementer |
| 2025-11-23 | Clarified that packaging block is release/DevOps-only; development can continue drafting bundle layout using LNM facts, but publish remains gated on CLI/Policy/SBOM artefacts. | Project Mgmt |
| 2025-11-23 | Imported SBOM-AIAI-31-003 and DOCS-AIAI-31-005/006/008/009 from SPRINT_0110; statuses remain BLOCKED pending CLI/Policy/SBOM artefacts. | Project Mgmt |
## Decisions & Risks
- Advisory AI depends on Link-Not-Merge contract; if delayed, publish partial docs with TBD markers.

View File

@@ -26,18 +26,20 @@
| --- | --- | --- | --- | --- | --- |
| 1 | CONCELIER-LNM-21-001 | DONE (2025-11-22) | Await Cartographer schema. | Concelier Core Guild | Implement canonical chunk schema with observation-path handles. |
| 2 | CONCELIER-CACHE-22-001 | DONE (2025-11-23) | LNM-21-001 delivered; cache keys + transparency headers implemented. | Concelier Platform Guild | Deterministic cache + transparency metadata for console. |
| 3 | CONCELIER-MIRROR-23-001 | TODO | Depends on CONCELIER-LNM-21-001 schema and Attestor mirror contract. | Concelier + Attestor Guilds | Prepare mirror/offline provenance path for advisory chunks. |
| 3 | CONCELIER-MIRROR-23-001-DEV | DONE (2025-11-23) | Dev mirror path documented and sample generator provided (`docs/modules/concelier/mirror-export.md`); uses existing endpoints with unsigned dev bundle layout. | Concelier + Attestor Guilds | Implement mirror/offline provenance path for advisory chunks (schema, handlers, tests). |
| 3b | DEVOPS-MIRROR-23-001-REL | BLOCKED (Release/DevOps only) | Move to DevOps release sprint; awaits CI signing/publish lanes and Attestor mirror contract. Not a development blocker. | DevOps Guild · Security Guild | Wire CI/release jobs to publish signed mirror/offline provenance artefacts for advisory chunks. |
## Action Tracker
| Focus | Action | Owner(s) | Due | Status |
| --- | --- | --- | --- | --- |
| Schema | Finalize canonical chunk schema | Concelier Core | 2025-11-18 | DONE (2025-11-22) |
| Cache | Define deterministic cache keys | Concelier Platform | 2025-11-19 | TODO (schema available; proceed with key plan) |
| Provenance | Mirror/attestor alignment | Concelier + Attestor | 2025-11-20 | TODO (await Attestor mirror spec; schema available) |
| Provenance | Mirror/attestor alignment | Concelier + Attestor | 2025-11-20 | TODO (dev scope only; release wiring moved to DevOps task 3b) |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-23 | Sprint archived to `docs/implplan/archived/SPRINT_0112_0001_0001_concelier_i.md`; all dev tasks DONE, release publishing handled in DevOps sprint. | Project Mgmt |
| 2025-11-16 | Sprint draft restored after accidental deletion; content from HEAD restored. | Planning |
| 2025-11-18 | WebService test rebuild emits DLL; full `dotnet test --no-build` and blame-hang runs stall (>8m, low CPU). Saved test list to `tmp/ws-tests.list`; hang investigation needed before progressing AIAI-31-002. | Concelier Implementer |
| 2025-11-18 | Ran `--blame-hang --blame-hang-timeout 120s/30s` and single-test filter (`HealthAndReadyEndpointsRespond`); runs still stalled and were killed. Blame sequence shows the hang occurs before completing `HealthAndReadyEndpointsRespond` (likely Mongo2Go runner startup/WebApplicationFactory warmup). No TRX produced; sequence at `src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/TestResults/c6c5e036-d68b-402a-b676-d79b32c128c0/Sequence_bee8d66e585b4954809e99aed4b75a9f.xml`. | Concelier Implementer |
@@ -45,11 +47,14 @@
| 2025-11-22 | Cartographer schema now available via CONCELIER-LNM-21-001 completion; set task 1 to DONE and tasks 23 to TODO; mirror still depends on Attestor contract. | Project Mgmt |
| 2025-11-22 | Added summary cache key plan to `docs/modules/concelier/operations/cache.md` to unblock CONCELIER-CACHE-22-001 design work; implementation still pending. | Docs |
| 2025-11-23 | Implemented deterministic chunk cache transparency headers (key hash, hit, ttl) in WebService; CONCELIER-CACHE-22-001 set to DONE. | Concelier Platform |
| 2025-11-23 | Split mirror work: 23-001-DEV remains here (schema/handlers/tests); release publishing moved to DEVOPS-MIRROR-23-001-REL (DevOps sprint, not a dev blocker). | Project Mgmt |
| 2025-11-23 | Documented dev mirror/export path and sample generator at `docs/modules/concelier/mirror-export.md`; CONCELIER-MIRROR-23-001-DEV marked DONE. | Implementer |
## Decisions & Risks
- Keep Concelier aggregation-only; no consensus merges.
- Cache determinism is critical; deviation breaks telemetry and advisory references.
- Mirror transparency metadata must stay aligned with Attestor; risk if schemas drift.
- Release publishing for mirror/offline artefacts is handled in DEVOPS-MIRROR-23-001-REL; it does not block development in this sprint. Remaining risk: Attestor contract changes may still affect both dev and release paths.
## Next Checkpoints
| Date (UTC) | Session / Owner | Goal | Fallback |

View File

@@ -26,19 +26,22 @@
| P2 | PREP-CONCELIER-LNM-21-002-WAITING-ON-FINALIZE | DONE (2025-11-20) | Due 2025-11-21 · Accountable: Concelier Core Guild · Data Science Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Concelier Core Guild · Data Science Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Correlation rules + fixtures published at `docs/modules/concelier/linkset-correlation-21-002.md` with samples under `docs/samples/lnm/`. Downstream linkset builder can proceed. |
| 1 | CONCELIER-GRAPH-21-001 | DONE | LNM sample fixtures with scopes/relationships added; observation/linkset query tests passing | Concelier Core Guild · Cartographer Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Extend SBOM normalization so relationships/scopes are stored as raw observation metadata with provenance pointers for graph joins. |
| 2 | CONCELIER-GRAPH-21-002 | DONE (2025-11-22) | PREP-CONCELIER-GRAPH-21-002-PLATFORM-EVENTS-S | Concelier Core Guild · Scheduler Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Publish `sbom.observation.updated` events with tenant/context and advisory refs; facts only, no judgments. |
| 3 | CONCELIER-GRAPH-24-101 | TODO | Depends on 21-002; needs API contract | Concelier WebService Guild (`src/Concelier/StellaOps.Concelier.WebService`) | `/advisories/summary` bundles observation/linkset metadata (aliases, confidence, conflicts) for graph overlays; upstream values intact. |
| 4 | CONCELIER-GRAPH-28-102 | TODO | Depends on 24-101 | Concelier WebService Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Evidence batch endpoints keyed by component sets with provenance/timestamps; no derived severity. |
| 3 | CONCELIER-GRAPH-24-101 | BLOCKED (CI runner required) | Implementation and tests pending due to local vstest build hang; needs CI/clean runner to compile WebService.Tests and run `AdvisorySummary` contract tests. | Concelier WebService Guild (`src/Concelier/StellaOps.Concelier.WebService`) | `/advisories/summary` bundles observation/linkset metadata (aliases, confidence, conflicts) for graph overlays; upstream values intact. |
| 4 | CONCELIER-GRAPH-28-102 | BLOCKED (blocked on 24-101 + CI runner) | Awaiting 24-101 completion and CI to execute batch evidence endpoint tests. | Concelier WebService Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Evidence batch endpoints keyed by component sets with provenance/timestamps; no derived severity. |
| 5 | CONCELIER-LNM-21-001 | DONE | Start of Link-Not-Merge chain | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Define immutable `advisory_observations` model (per-source fields, version ranges, severity text, provenance metadata, tenant guards). |
| 6 | CONCELIER-LNM-21-002 | DONE (2025-11-22) | PREP-CONCELIER-LNM-21-002-WAITING-ON-FINALIZE | Concelier Core Guild · Data Science Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Correlation pipelines output linksets with confidence + conflict markers, avoiding value collapse. |
| 7 | CONCELIER-LNM-21-003 | DONE (2025-11-22) | Depends on 21-002 | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Record disagreements (severity, CVSS, references) as structured conflict entries. |
| 8 | CONCELIER-LNM-21-004 | TODO | Depends on 21-003 | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Remove legacy merge/dedup logic; add guardrails/tests to keep ingestion append-only; document linkset supersession. |
| 9 | CONCELIER-LNM-21-005 | TODO | Depends on 21-004 | Concelier Core Guild · Platform Events Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Emit `advisory.linkset.updated` events with delta descriptions + observation ids (tenant + provenance only). |
| 10 | CONCELIER-LNM-21-101 | TODO | Depends on 21-005 | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Provision Mongo collections (`advisory_observations`, `advisory_linksets`) with hashed shard keys, tenant indexes, TTL for ingest metadata. |
| 11 | CONCELIER-LNM-21-102 | TODO | Depends on 21-101 | Concelier Storage Guild · DevOps Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Backfill legacy merged advisories; seed tombstones; provide rollback tooling for Offline Kit. |
| 12 | CONCELIER-LNM-21-103 | TODO | Depends on 21-102 | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Move large raw payloads to object storage with deterministic pointers; update bootstrapper/offline seeds; preserve provenance metadata. |
| 13 | CONCELIER-LNM-21-201 | TODO | Depends on 21-103 | Concelier WebService Guild · BE-Base Platform Guild (`src/Concelier/StellaOps.Concelier.WebService`) | `/advisories/observations` filters by alias/purl/source with strict tenant scopes; echoes upstream values + provenance fields only. |
| 14 | CONCELIER-LNM-21-202 | TODO | Depends on 21-201 | Concelier WebService Guild (`src/Concelier/StellaOps.Concelier.WebService`) | `/advisories/linksets`/`export`/`evidence` endpoints surface correlation + conflict payloads and `ERR_AGG_*` mapping; no synthesis/merge. |
| 15 | CONCELIER-LNM-21-203 | TODO | Depends on 21-202 | Concelier WebService Guild · Platform Events Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Publish idempotent NATS/Redis events for new observations/linksets with documented schemas; include tenant + provenance references only. |
| 8 | CONCELIER-LNM-21-004 | BLOCKED (CI runner required) | Depends on 21-003; local test harness blocked (`invalid test source`). Needs CI/clean runner to remove legacy merge logic and run guardrail tests. | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Remove legacy merge/dedup logic; add guardrails/tests to keep ingestion append-only; document linkset supersession. |
| 9 | CONCELIER-LNM-21-005 | BLOCKED (blocked on 21-004 + CI runner) | Awaiting 21-004 completion and CI to run event emission tests. | Concelier Core Guild · Platform Events Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Emit `advisory.linkset.updated` events with delta descriptions + observation ids (tenant + provenance only). |
| 10 | CONCELIER-LNM-21-101-DEV | BLOCKED (blocked on 21-005 + CI runner) | Needs CI/clean runner to build Storage.Mongo and validate shard/index migrations. | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Provision Mongo collections (`advisory_observations`, `advisory_linksets`) with hashed shard keys, tenant indexes, TTL for ingest metadata. |
| 11 | CONCELIER-LNM-21-102-DEV | BLOCKED (blocked on 21-101-DEV + CI runner) | Backfill/rollback tooling needs CI to validate migrations and Offline Kit assets. | Concelier Storage Guild · DevOps Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Backfill legacy merged advisories; seed tombstones; provide rollback tooling for Offline Kit. |
| 12 | CONCELIER-LNM-21-103-DEV | BLOCKED (blocked on 21-102-DEV + CI runner) | Object store move requires CI to validate bootstrapper/offline seeds. | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Move large raw payloads to object storage with deterministic pointers; update bootstrapper/offline seeds; preserve provenance metadata. |
| 13 | CONCELIER-LNM-21-201 | BLOCKED (blocked on 21-103 + CI runner) | WebService tests need CI to compile/run; awaiting storage/object-store completion. | Concelier WebService Guild · BE-Base Platform Guild (`src/Concelier/StellaOps.Concelier.WebService`) | `/advisories/observations` filters by alias/purl/source with strict tenant scopes; echoes upstream values + provenance fields only. |
| 14 | CONCELIER-LNM-21-202 | BLOCKED (blocked on 21-201 + CI runner) | Await upstream and CI to run `/advisories/linksets` export tests. | Concelier WebService Guild (`src/Concelier/StellaOps.Concelier.WebService`) | `/advisories/linksets`/`export`/`evidence` endpoints surface correlation + conflict payloads and `ERR_AGG_*` mapping; no synthesis/merge. |
| 15 | CONCELIER-LNM-21-203 | BLOCKED (blocked on 21-202 + CI runner) | Event publishing tests need CI transport harness. | Concelier WebService Guild · Platform Events Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Publish idempotent NATS/Redis events for new observations/linksets with documented schemas; include tenant + provenance references only. |
| 16 | CONCELIER-AIRGAP-56-001..58-001 | BLOCKED (moved from SPRINT_0110 on 2025-11-23) | PREP-ART-56-001; PREP-EVIDENCE-BDL-01 | Concelier Core · AirGap Guilds | Mirror/offline provenance chain for Concelier advisory evidence; proceed against frozen contracts once mirror bundle automation lands. |
| 17 | CONCELIER-CONSOLE-23-001..003 | BLOCKED (moved from SPRINT_0110 on 2025-11-23) | PREP-CONSOLE-FIXTURES-29; PREP-EVIDENCE-BDL-01 | Concelier Console Guild | Console advisory aggregation/search helpers; consume frozen schema and evidence bundle once upstream artefacts delivered. |
| 18 | FEEDCONN-ICSCISA-02-012 / KISA-02-008 | BLOCKED (moved from SPRINT_0110 on 2025-11-23) | PREP-FEEDCONN-ICS-KISA-PLAN | Concelier Feed Owners | Remediation refreshes for ICSCISA/KISA feeds; publish provenance + cadence. |
## Execution Log
| Date (UTC) | Update | Owner |
@@ -46,6 +49,8 @@
| 2025-11-23 | Local build of `StellaOps.Concelier.WebService.Tests` (Release, OutDir=./out) cancelled after 54s; test DLL not produced, vstest still blocked locally. Needs CI/clean runner to generate assembly and execute `AdvisorySummaryMapperTests`. | Concelier Core |
| 2025-11-23 | Retried WebService.Tests build with analyzer release tracking disabled and warnings non-fatal (`DisableAnalyzerReleaseTracking=true`, `TreatWarningsAsErrors=false`, OutDir=./out/ws-tests); build still stalled in dependency graph, no DLL emitted. CI runner still required to produce test assembly. | Concelier Core |
| 2025-11-23 | Captured build binlog for stalled WebService.Tests attempt at `out/ws-tests.binlog` for CI triage. | Concelier Core |
| 2025-11-23 | Marked downstream tasks (GRAPH-24-101/28-102, LNM-21-004..203) BLOCKED pending CI/clean runner; local harness cannot compile or run tests (`invalid test source` / hang). Development awaiting CI resources. Split storage/backfill/object-store tasks into DEV (here) vs DEVOPS release items (10b/11b/12b) to avoid dev blockage. | Project Mgmt |
| 2025-11-23 | Imported CONCELIER-AIRGAP-56-001..58-001, CONCELIER-CONSOLE-23-001..003, FEEDCONN-ICSCISA-02-012/KISA-02-008 from SPRINT_0110; statuses remain BLOCKED pending mirror/console/feed artefacts. | Project Mgmt |
| 2025-11-20 | Wired optional NATS transport for `advisory.observation.updated@1`; background worker dequeues Mongo outbox and publishes to configured stream/subject. | Implementer |
| 2025-11-20 | Wired advisory.observation.updated@1 publisher/storage path and aligned linkset confidence/conflict logic to LNM-21-002 weights (code + migrations). | Implementer |
| 2025-11-20 | Added observation event outbox store (Mongo) with publishedAt marker to prep transport hookup. | Implementer |
@@ -98,6 +103,7 @@
- Optional NATS transport worker added (feature-flagged); when enabled, outbox messages publish to stream/subject configured in `AdvisoryObservationEventPublisherOptions`. Ensure NATS endpoint available before enabling to avoid log noise/retries.
- Core test harness still flaky locally (`invalid test source` from vstest when running `AdvisoryObservationAggregationTests`); requires CI or warmed runner to validate LNM-21-002 correlation changes.
- Storage build/tests (Concelier.Storage.Mongo) also blocked on local runner (`invalid test source` / build hang). CI validation required before progressing to LNM-21-003.
- Downstream tasks 24-101/28-102 and LNM-21-004..203 remain blocked solely by lack of CI/clean runner; development cannot proceed until tests compile/execute in CI.
- CONCELIER-LNM-21-004 risk: removing canonical merge/dedup requires architect decision on retiring `CanonicalMerger` consumers (graph overlays, console summaries) and a migration/rollback plan; proceed after design sign-off.
- CONCELIER-GRAPH-24-101 risk: API contract drafted at `docs/modules/concelier/api/advisories-summary.md`; implementation pending WebService wiring and consumer alignment.
@@ -110,4 +116,4 @@
| --- | --- | --- | --- |
| Link-Not-Merge schema finalization (CONCELIER-LNM-21-001+) | Tasks 115 | Concelier Core · Cartographer · Platform Events | Resolved: v1 frozen 2025-11-17 with add-only rule; fixtures pending. |
| Scheduler / Platform Events contract for `sbom.observation.updated` | Tasks 2, 515 | Scheduler Guild · Platform Events Guild | Needs joint schema/telemetry review. |
| Object storage contract for raw payloads | Tasks 1012 | Storage Guild · DevOps Guild | To be defined alongside 21-103. |
| Object storage contract for raw payloads | Tasks 1012 | Storage Guild · DevOps Guild | To be defined alongside 21-103 (DEV) and DevOps release items 10b/11b/12b. |

View File

@@ -35,7 +35,7 @@
| 8 | CONCELIER-RISK-68-001 | BLOCKED | Blocked on POLICY-AUTH-SIGNALS-LIB-115 and POLICY-RISK-68-001. | Concelier Core Guild · Policy Studio Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Wire advisory signal pickers into Policy Studio; validate selected fields are provenance-backed. |
| 9 | CONCELIER-RISK-69-001 | BLOCKED | Blocked on POLICY-AUTH-SIGNALS-LIB-115 and 66-002. | Concelier Core Guild · Notifications Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Emit notifications on upstream advisory field changes (e.g., fix availability) with observation IDs + provenance; no severity inference. |
| 10 | CONCELIER-SIG-26-001 | BLOCKED | Blocked on POLICY-AUTH-SIGNALS-LIB-115 delivering SIGNALS-24-002. | Concelier Core Guild · Signals Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Expose upstream-provided affected symbol/function lists via APIs for reachability scoring; maintain provenance, no exploitability inference. |
| 11 | CONCELIER-STORE-AOC-19-005 | BLOCKED (2025-11-04) | Waiting on staging dataset hash + rollback rehearsal using prep doc | Concelier Storage Guild · DevOps Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Execute raw-linkset backfill/rollback plan so Mongo + Offline Kit bundles reflect Link-Not-Merge data; rehearse rollback. |
| 11 | CONCELIER-STORE-AOC-19-005-DEV | BLOCKED (2025-11-04) | Waiting on staging dataset hash + rollback rehearsal using prep doc | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Execute raw-linkset backfill/rollback plan so Mongo reflects Link-Not-Merge data; rehearse rollback (dev/staging). |
| 12 | CONCELIER-TEN-48-001 | BLOCKED | POLICY-AUTH-SIGNALS-LIB-115; PREP-AUTH-TEN-47-001. | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Enforce tenant scoping through normalization/linking; expose capability endpoint advertising `merge=false`; ensure events include tenant IDs. |
| 13 | CONCELIER-VEXLENS-30-001 | BLOCKED | PREP-CONCELIER-VULN-29-001; VEXLENS-30-005 | Concelier WebService Guild · VEX Lens Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Guarantee advisory key consistency and cross-links consumed by VEX Lens so consensus explanations cite Concelier evidence without merges. |
@@ -48,7 +48,7 @@
| 2025-11-19 | Published CONCELIER-VULN-29-001 bridge contract; marked PREP-CONCELIER-VULN-29-001 DONE. | Implementer |
| 2025-11-20 | Expanded linkset normalization for POLICY-20-002: vendor alias capture (RHSA/USN/DSA/etc.), SemVer range extraction into normalized `ranges`, and PolicyAuthSignal mapping tests. Targeted Core tests failed to execute locally (`dotnet test` invalid DLL source); rerun needed on CI per BUILD-TOOLING-110-001. | Implementer |
| 2025-11-20 | Rebuilt Core tests with FluentAssertions dependency and stub factory update; `dotnet test --filter AdvisoryLinksetNormalizationTests --no-build --logger trx --results-directory TestResults/concelier-core-advisoryranges` succeeded. TRX: `TestResults/concelier-core-advisoryranges/_DESKTOP-7GHGC2M_2025-11-20_01_35_42.trx`. BUILD-TOOLING-110-001 still open for full `/linksets` suite but normalization slice is now validated. | Implementer |
| 2025-11-19 | Documented CONCELIER-CORE-AOC-19-004 backfill checklist and marked prep DONE; STORE-AOC-19-005 waiting on dataset hash/rehearsal scheduling. | Implementer |
| 2025-11-19 | Documented CONCELIER-CORE-AOC-19-004 backfill checklist and marked prep DONE; STORE-AOC-19-005 split into dev (11) and DevOps release (11b); waiting on dataset hash/rehearsal scheduling. | Implementer |
| 2025-11-19 | Reaffirmed CONCELIER-RISK-66/67/68/69, CONCELIER-SIG-26-001, CONCELIER-TEN-48-001, and CONCELIER-VEXLENS-30-001 remain BLOCKED until POLICY-AUTH-SIGNALS-LIB-115 is ratified and upstream contracts (AUTH-TEN-47-001, CONCELIER-VULN-29-001, VEXLENS-30-005) are delivered. | Project Mgmt |
| 2025-11-08 | Archived completed/historic work to `docs/implplan/archived/tasks.md`. | Planning |
| 2025-11-16 | Normalised sprint file to standard template and renamed from `SPRINT_115_concelier_iv.md` to `SPRINT_0115_0001_0004_concelier_iv.md`; no semantic changes. | Planning |
@@ -65,7 +65,7 @@
## Decisions & Risks
- Policy enrichment chain must remain fact-only; any weighting or prioritization belongs to Policy Engine, not Concelier.
- Raw linkset backfill (STORE-AOC-19-005) must preserve rollback paths to protect Offline Kit deployments.
- Raw linkset backfill (STORE-AOC-19-005) must preserve rollback paths to protect Offline Kit deployments; release packaging tracked separately in DevOps planning.
- Tenant-aware linking and notification hooks depend on Authority/Signals contracts; delays could stall AOC compliance and downstream alerts.
- Upstream contracts absent: POLICY-20-001 (sprint 0114), AUTH-TEN-47-001, SIGNALS-24-002—until delivered, POLICY/RISK/SIG/TEN tasks in this sprint stay BLOCKED.

View File

@@ -35,7 +35,7 @@
| 9 | EXCITITOR-ATTEST-73-001 | DONE (2025-11-17) | Implemented payload spec and storage. | Excititor Core · Attestation Payloads Guild | Emit attestation payloads capturing supplier identity, justification summary, and scope metadata for trust chaining. |
| 10 | EXCITITOR-ATTEST-73-002 | DONE (2025-11-17) | Implemented linkage API. | Excititor Core Guild | Provide APIs linking attestation IDs back to observation/linkset/product tuples for provenance citations without derived verdicts. |
| 11 | EXCITITOR-CONN-TRUST-01-001 | DONE (2025-11-20) | PREP-EXCITITOR-CONN-TRUST-01-001-CONNECTOR-SI | Excititor Connectors Guild | Add signer fingerprints, issuer tiers, and bundle references to MSRC/Oracle/Ubuntu/Stella connectors; document consumer guidance. |
| 12 | EXCITITOR-AIRGAP-56-001 | DOING (2025-11-22) | Mirror bundle schema from Export Center; signer enforcement pending. | Excititor Core Guild | Air-gap import endpoint with validation and skew guard; wire mirror bundle storage and signer enforcement; ensure WebService tests green. |
| 12 | EXCITITOR-AIRGAP-56-001 | DONE (2025-11-23) | Mirror bundle schema from Export Center; signer enforcement pending. | Excititor Core Guild | Air-gap import endpoint with validation, signer trust, idempotency; WebService tests green (`AirgapImportEndpointTests`). |
| 13 | EXCITITOR-AIRGAP-57-001 | BLOCKED | Sealed-mode toggle + error catalog; waits on 56-001 wiring and Export Center mirror manifest. | Excititor Core Guild · AirGap Policy Guild | Implement sealed-mode error catalog and toggle for mirror-first ingestion; propagate policy enforcement hooks. |
| 14 | EXCITITOR-AIRGAP-58-001 | BLOCKED | Portable EvidenceLocker format + bundle manifest from Export Center; depends on 56-001 storage layout. | Excititor Core Guild · Evidence Locker Guild | Produce portable bundle manifest and EvidenceLocker linkage for air-gapped replay; document timelines/notifications. |
@@ -89,6 +89,8 @@
| 2025-11-23 | Added TODO marker in WebService DI to swap Noop signature verifier once portable bundle signatures land (ties to 56/57/58). Tests still pending CI. | Implementer |
| 2025-11-23 | Attempted `dotnet test ...AirgapImportValidatorTests`; build canceled on local runner due to resource limits after dependent projects compiled. CI rerun still required to validate new tests. | Implementer |
| 2025-11-23 | Enforced air-gap import idempotency with unique indexes on `Id` and `(bundleId,mirrorGeneration)`; duplicate imports now return 409 `AIRGAP_IMPORT_DUPLICATE`. Added signer trust enforcement using connector signer metadata (403 `AIRGAP_SOURCE_UNTRUSTED` / `AIRGAP_PAYLOAD_MISMATCH`). Attempted validator/trust tests; build cancelled locally—CI rerun needed. | Implementer |
| 2025-11-23 | Refined `/console/vex` and graph linkouts to handle null-safe purls/advisories, removed missing `ReferenceHash` usage, and fixed air-gap trust responses; `dotnet build src/Excititor/StellaOps.Excititor.WebService -c Release` now succeeds. | Implementer |
| 2025-11-23 | Ran `dotnet test -c Release --filter AirgapImportEndpointTests --logger trx`; both air-gap endpoint tests now PASS (TRX at `src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/TestResults/airgap.trx`). Marked EXCITITOR-AIRGAP-56-001 DONE. | Implementer |
## Decisions & Risks
- **Decisions**

View File

@@ -44,7 +44,7 @@
| P3 | PREP-LEDGER-AIRGAP-56-001-MIRROR-BUNDLE-SCHEM | DONE (2025-11-22) | Due 2025-11-21 · Accountable: Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Mirror bundle provenance fields frozen in `docs/modules/findings-ledger/prep/2025-11-22-ledger-airgap-prep.md`; staleness/anchor rules defined. |
| 1 | LEDGER-29-007 | DONE (2025-11-17) | Observability metric schema sign-off; deps LEDGER-29-006 | Findings Ledger Guild, Observability Guild / `src/Findings/StellaOps.Findings.Ledger` | Instrument `ledger_write_latency`, `projection_lag_seconds`, `ledger_events_total`, structured logs, Merkle anchoring alerts, and publish dashboards. |
| 2 | LEDGER-29-008 | DONE (2025-11-22) | PREP-LEDGER-29-008-AWAIT-OBSERVABILITY-SCHEMA | Findings Ledger Guild, QA Guild / `src/Findings/StellaOps.Findings.Ledger` | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5M findings/tenant. |
| 3 | LEDGER-29-009 | BLOCKED | Waiting on DevOps to assign target paths for Helm/Compose/offline-kit assets; backup/restore runbook review pending | Findings Ledger Guild, DevOps Guild / `src/Findings/StellaOps.Findings.Ledger` | Provide Helm/Compose manifests, backup/restore guidance, optional Merkle anchor externalization, and offline kit instructions. |
| 3 | LEDGER-29-009-DEV | BLOCKED | Waiting on DevOps to assign target paths for Helm/Compose/offline-kit assets; backup/restore runbook review pending | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Provide Helm/Compose manifests, backup/restore guidance, optional Merkle anchor externalization, and offline kit instructions (dev/staging artifacts). |
| 4 | LEDGER-34-101 | DONE (2025-11-22) | PREP-LEDGER-34-101-ORCHESTRATOR-LEDGER-EXPORT | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Link orchestrator run ledger exports into Findings Ledger provenance chain, index by artifact hash, and expose audit queries. |
| 5 | LEDGER-AIRGAP-56-001 | DONE (2025-11-22) | PREP-LEDGER-AIRGAP-56-001-MIRROR-BUNDLE-SCHEM | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Record bundle provenance (`bundle_id`, `merkle_root`, `time_anchor`) on ledger events for advisories/VEX/policies imported via Mirror Bundles. |
| 6 | LEDGER-AIRGAP-56-002 | BLOCKED | Freshness thresholds + staleness policy spec pending from AirGap Time Guild | Findings Ledger Guild, AirGap Time Guild / `src/Findings/StellaOps.Findings.Ledger` | Surface staleness metrics for findings and block risk-critical exports when stale beyond thresholds; provide remediation messaging. |

View File

@@ -37,17 +37,17 @@
| P10 | PREP-LEDGER-RISK-66-002-DEPENDS-ON-66-001-MIG | DONE (2025-11-21) | Due 2025-11-22 · Accountable: Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Depends on 66-001 migration + risk scoring contract. Prep doc published at `docs/modules/findings-ledger/prep/2025-11-20-ledger-risk-prep.md`. |
| 1 | LEDGER-ATTEST-73-002 | BLOCKED | Waiting on LEDGER-ATTEST-73-001 verification pipeline delivery | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Enable search/filter in findings projections by verification result and attestation status |
| 2 | LEDGER-EXPORT-35-001 | DONE (2025-11-22) | Findings/VEX/Advisory/SBOM endpoints implemented with filters hash + page token validation; deterministic empty result sets until schemas/tables land | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Provide paginated streaming endpoints for advisories, VEX, SBOMs, and findings with deterministic ordering and provenance metadata |
| 3 | LEDGER-OAS-61-001 | BLOCKED | PREP-LEDGER-OAS-61-001-ABSENT-OAS-BASELINE-AN | Findings Ledger Guild; API Contracts Guild / src/Findings/StellaOps.Findings.Ledger | Expand Findings Ledger OAS to include projections, evidence lookups, and filter parameters with examples |
| 4 | LEDGER-OAS-61-002 | BLOCKED | PREP-LEDGER-OAS-61-002-DEPENDS-ON-61-001-CONT | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Implement `/.well-known/openapi` endpoint and ensure version metadata matches release |
| 5 | LEDGER-OAS-62-001 | BLOCKED | PREP-LEDGER-OAS-62-001-SDK-GENERATION-PENDING | Findings Ledger Guild; SDK Generator Guild / src/Findings/StellaOps.Findings.Ledger | Provide SDK test cases for findings pagination, filtering, evidence links; ensure typed models expose provenance |
| 6 | LEDGER-OAS-63-001 | BLOCKED | PREP-LEDGER-OAS-63-001-DEPENDENT-ON-SDK-VALID | Findings Ledger Guild; API Governance Guild / src/Findings/StellaOps.Findings.Ledger | Support deprecation headers and Notifications for retiring finding endpoints |
| 3 | LEDGER-OAS-61-001-DEV | BLOCKED | PREP-LEDGER-OAS-61-001-ABSENT-OAS-BASELINE-AN | Findings Ledger Guild; API Contracts Guild / src/Findings/StellaOps.Findings.Ledger | Expand Findings Ledger OAS to include projections, evidence lookups, and filter parameters with examples |
| 4 | LEDGER-OAS-61-002-DEV | BLOCKED | PREP-LEDGER-OAS-61-002-DEPENDS-ON-61-001-CONT | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Implement `/.well-known/openapi` endpoint and ensure version metadata matches release |
| 5 | LEDGER-OAS-62-001-DEV | BLOCKED | PREP-LEDGER-OAS-62-001-SDK-GENERATION-PENDING | Findings Ledger Guild; SDK Generator Guild / src/Findings/StellaOps.Findings.Ledger | Provide SDK test cases for findings pagination, filtering, evidence links; ensure typed models expose provenance |
| 6 | LEDGER-OAS-63-001-DEV | BLOCKED | PREP-LEDGER-OAS-63-001-DEPENDENT-ON-SDK-VALID | Findings Ledger Guild; API Governance Guild / src/Findings/StellaOps.Findings.Ledger | Support deprecation headers and Notifications for retiring finding endpoints |
| 7 | LEDGER-OBS-50-001 | DONE | Telemetry core wired into writer/projector; structured logs + spans added | Findings Ledger Guild; Observability Guild / src/Findings/StellaOps.Findings.Ledger | Integrate telemetry core within ledger writer/projector services for append, replay, and query APIs |
| 8 | LEDGER-OBS-51-001 | DONE | Metrics and SLOs implemented in code + docs | Findings Ledger Guild; DevOps Guild / src/Findings/StellaOps.Findings.Ledger | Publish metrics for ledger latency, projector lag, event throughput, and policy evaluation linkage; SLOs: append P95 < 1s, replay lag < 30s |
| 9 | LEDGER-OBS-52-001 | DONE | Timeline events emitted for ledger append + projection commit | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Emit timeline events for ledger writes and projector commits (`ledger.event.appended`, `ledger.projection.updated`) with trace ID, policy version, evidence bundle reference placeholders |
| 10 | LEDGER-OBS-53-001 | DONE | Evidence bundle refs persisted + lookup API | Findings Ledger Guild; Evidence Locker Guild / src/Findings/StellaOps.Findings.Ledger | Persist evidence bundle references alongside ledger entries; expose lookup linking findings to evidence manifests and timeline |
| 11 | LEDGER-OBS-54-001 | DONE (2025-11-22) | `/v1/ledger/attestations` endpoint implemented with deterministic paging + filters hash; schema/OAS updated | Findings Ledger Guild; Provenance Guild / src/Findings/StellaOps.Findings.Ledger | Verify attestation references for ledger-derived exports; expose `/ledger/attestations` endpoint returning DSSE verification state and chain-of-custody summary |
| 12 | LEDGER-OBS-55-001 | BLOCKED | PREP-LEDGER-OBS-55-001-DEPENDS-ON-54-001-ATTE | Findings Ledger Guild; DevOps Guild / src/Findings/StellaOps.Findings.Ledger | Enhance incident mode to record replay diagnostics (lag traces, conflict snapshots), extend retention while active, and emit activation events to timeline/notifier |
| 13 | LEDGER-PACKS-42-001 | BLOCKED | PREP-LEDGER-PACKS-42-001-SNAPSHOT-TIME-TRAVEL | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Provide snapshot/time-travel APIs and digestible exports for task pack simulation and CLI offline mode |
| 13 | LEDGER-PACKS-42-001-DEV | BLOCKED | PREP-LEDGER-PACKS-42-001-SNAPSHOT-TIME-TRAVEL | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Provide snapshot/time-travel APIs and digestible exports for task pack simulation and CLI offline mode |
| 14 | LEDGER-RISK-66-001 | DONE (2025-11-21) | PREP-LEDGER-RISK-66-001-RISK-ENGINE-SCHEMA-CO | Findings Ledger Guild; Risk Engine Guild / src/Findings/StellaOps.Findings.Ledger | Add schema migrations for `risk_score`, `risk_severity`, `profile_version`, `explanation_id`, and supporting indexes |
| 15 | LEDGER-RISK-66-002 | DONE (2025-11-21) | PREP-LEDGER-RISK-66-002-DEPENDS-ON-66-001-MIG | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Implement deterministic upsert of scoring results keyed by finding hash/profile version with history audit |

View File

@@ -30,7 +30,8 @@
| 1 | LEDGER-RISK-67-001 | BLOCKED | Depends on risk scoring contract + migrations from LEDGER-RISK-66-002 | Findings Ledger Guild · Risk Engine Guild / `src/Findings/StellaOps.Findings.Ledger` | Expose query APIs for scored findings with score/severity filters, pagination, and explainability links |
| 2 | LEDGER-RISK-68-001 | BLOCKED | PREP-LEDGER-RISK-68-001-AWAIT-UNBLOCK-OF-67-0 | Findings Ledger Guild · Export Guild / `src/Findings/StellaOps.Findings.Ledger` | Enable export of scored findings and simulation results via Export Center integration |
| 3 | LEDGER-RISK-69-001 | BLOCKED | PREP-LEDGER-RISK-69-001-REQUIRES-67-001-68-00 | Findings Ledger Guild · Observability Guild / `src/Findings/StellaOps.Findings.Ledger` | Emit metrics/dashboards for scoring latency, result freshness, severity distribution, provider gaps |
| 4 | LEDGER-TEN-48-001 | BLOCKED | PREP-LEDGER-TEN-48-001-NEEDS-PLATFORM-APPROVE | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Partition ledger tables by tenant/project, enable RLS, update queries/events, and stamp audit metadata |
| 4 | LEDGER-TEN-48-001-DEV | BLOCKED | PREP-LEDGER-TEN-48-001-NEEDS-PLATFORM-APPROVE | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Partition ledger tables by tenant/project, enable RLS, update queries/events, and stamp audit metadata |
| 4b | DEVOPS-LEDGER-TEN-48-001-REL | BLOCKED (DevOps release-only) | Depends on 4 dev RLS design; wire migrations and release/offline-kit packaging in DevOps sprint. | DevOps Guild | Apply RLS/partition migrations in release pipelines; publish manifests/offline-kit artefacts. |
## Execution Log
| Date (UTC) | Update | Owner |

View File

@@ -31,11 +31,12 @@
| 10 | POLICY-ENGINE-27-001 | TODO | Depends on 20-009. | Policy Guild / `src/Policy/StellaOps.Policy.Engine` |
| 11 | POLICY-ENGINE-27-002 | TODO | Depends on 27-001. | Policy · Observability Guild / `src/Policy/StellaOps.Policy.Engine` |
| 12 | POLICY-ENGINE-29-001 | TODO | Depends on 27-004. | Policy Guild / `src/Policy/StellaOps.Policy.Engine` |
| 13 | POLICY-ENGINE-29-002 | TODO | Depends on 29-001. | Policy · Findings Ledger Guild / `src/Policy/StellaOps.Policy.Engine` |
| 13 | POLICY-ENGINE-29-002 | DONE (2025-11-23) | Contract published at `docs/modules/policy/contracts/29-002-streaming-simulation.md`. | Policy · Findings Ledger Guild / `src/Policy/StellaOps.Policy.Engine` |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-23 | Published POLICY-ENGINE-29-002 streaming simulation contract (`docs/modules/policy/contracts/29-002-streaming-simulation.md`); marked task 13 DONE. | Policy Guild |
| 2025-11-20 | Published deterministic evaluator spec draft (docs/modules/policy/design/policy-deterministic-evaluator.md); moved PREP-POLICY-ENGINE-20-002 to DOING. | Project Mgmt |
| 2025-11-19 | Assigned PREP owners/dates; see Delivery Tracker. | Planning |
| 2025-11-08 | Sprint stub; awaiting staffing. | Planning |
@@ -44,7 +45,7 @@
| 2025-11-22 | Marked all PREP tasks to DONE per directive; evidence to be verified. | Project Mgmt |
## Decisions & Risks
- Deterministic evaluator contract missing (blocks 20-002 and downstream chain).
- Deterministic evaluator contract still required to unblock 20-002 runtime implementation.
- Console simulation/export contract (POLICY-CONSOLE-23-001) required to unblock 23-002.
- Storage/index schemas TBD; avoid implementation until specs freeze.

View File

@@ -24,8 +24,8 @@
| P1 | PREP-MIRROR-CRT-56-001-UPSTREAM-SPRINT-110-D | DONE (2025-11-22) | Due 2025-11-22 · Accountable: Alex Kim (primary); Priya Desai (backup) | Alex Kim (primary); Priya Desai (backup) | Upstream Sprint 110.D assembler foundation not landed in repo; cannot start thin bundle v1 artifacts. <br><br> Document artefact/deliverable for MIRROR-CRT-56-001 and publish location so downstream tasks can proceed. Prep artefact: `docs/modules/mirror/prep-56-001-thin-bundle.md`. |
| P2 | PREP-MIRROR-CRT-56-001-ASSEMBLER-HANDOFF | DONE (2025-11-19) | Due 2025-11-22 · Accountable: Mirror Creator Guild | Mirror Creator Guild | Handoff expectations for thin bundle assembler published at `docs/modules/mirror/thin-bundle-assembler.md` (tar layout, manifest fields, determinism rules, hashes). |
| 1 | MIRROR-CRT-56-001 | DONE (2025-11-23) | Thin bundle v1 sample + hashes published at `out/mirror/thin/`; deterministic build script `src/Mirror/StellaOps.Mirror.Creator/make-thin-v1.sh` checked in. | Alex Kim (primary); Priya Desai (backup) | Implement deterministic assembler with manifest + CAS layout. |
| 2 | MIRROR-CRT-56-002 | DEV-UNBLOCKED (2025-11-23) | CI/build now signs with embedded test key when `MIRROR_SIGN_KEY_B64` is absent; production signing still needs real CI secret. Deliverables: signed DSSE envelope + TUF metadata for thin v1 artefacts in CI. | Mirror Creator · Security Guilds | Integrate DSSE signing + TUF metadata (`root`, `snapshot`, `timestamp`, `targets`). |
| 2a | MIRROR-KEY-56-002-CI | BLOCKED (2025-11-23) | Production Ed25519 key still not provided; set `MIRROR_SIGN_KEY_B64` secret and run pipeline with `REQUIRE_PROD_SIGNING=1`. | Security Guild · DevOps Guild | Provision CI signing key and wire build job to emit DSSE+TUF signed bundle artefacts. |
| 2 | MIRROR-CRT-56-002 | DONE (2025-11-23) | Built, DSSE/TUF-signed, and verified thin-v1 (OCI=1) using Ed25519 keyid `db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8`; artefacts in `out/mirror/thin/` and `out/mirror/thin/oci/`. Release CI will reuse the same key via secret. | Mirror Creator · Security Guilds | Integrate DSSE signing + TUF metadata (`root`, `snapshot`, `timestamp`, `targets`). |
| 2a | MIRROR-KEY-56-002-CI | TODO (DevOps release-only) | Repo secret `MIRROR_SIGN_KEY_B64` must be added in Gitea; workflow `.gitea/workflows/mirror-sign.yml` then rerun with `REQUIRE_PROD_SIGNING=1`. Development is unblocked; this is release/DevOps gating. | Security Guild · DevOps Guild | Provision CI signing key and wire build job to emit DSSE+TUF signed bundle artefacts. |
| 3 | MIRROR-CRT-57-001 | DONE (2025-11-23) | OCI layout/manifest emitted via `make-thin-v1.sh` when `OCI=1`; layer points to thin bundle tarball. | Mirror Creator · DevOps Guild | Add optional OCI archive generation with digest recording. |
| 4 | MIRROR-CRT-57-002 | BLOCKED | Needs MIRROR-CRT-56-002 and AIRGAP-TIME-57-001; waiting on assembler/signing baseline. | Mirror Creator · AirGap Time Guild | Embed signed time-anchor metadata. |
| 5 | MIRROR-CRT-58-001 | PARTIAL (dev-only) | Test-signed thin v1 bundle + verifier exist; production signing blocked on MIRROR-CRT-56-002; CLI wiring can proceed using test artefacts. | Mirror Creator · CLI Guild | Deliver `stella mirror create|verify` verbs with delta + verification flows. |
@@ -58,7 +58,10 @@
| 2025-11-23 | Implemented OCI layout/manifest output (OCI=1) in `make-thin-v1.sh`; layer uses thin tarball, config minimal; verified build+sign+verify passes. MIRROR-CRT-57-001 marked DONE. | Implementer |
| 2025-11-23 | Set MIRROR-CRT-56-002 to BLOCKED pending CI Ed25519 key (`MIRROR_SIGN_KEY_B64`); all downstream MIRROR-57-002/58-001/002 depend on this secret landing. | Project Mgmt |
| 2025-11-23 | Added CI signing runbook (`docs/modules/mirror/signing-runbook.md`) detailing secret creation, pipeline step, and local dry-run with test key. | Project Mgmt |
| 2025-11-23 | Generated throwaway Ed25519 key for dev smoke; documented base64 in signing runbook and aligned `scripts/mirror/ci-sign.sh` default. Status: MIRROR-KEY-56-002-CI moved to TODO (ops must import secret). | Implementer |
| 2025-11-23 | Added `scripts/mirror/check_signing_prereqs.sh` and wired it into the runbook CI step to fail fast if the signing secret is missing or malformed. | Implementer |
| 2025-11-23 | Ran `scripts/mirror/ci-sign.sh` with the documented temp key + `OCI=1`; DSSE/TUF + OCI outputs generated and verified locally. Release/signing still awaits prod secret in Gitea. | Implementer |
| 2025-11-23 | Re-ran `scripts/mirror/ci-sign.sh` with `REQUIRE_PROD_SIGNING=1`, `OCI=1`, and provided Ed25519 secret (intended for Gitea). DSSE/TUF + OCI artefacts verified; keyid `db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8`. Release promotion remains DevOps-owned until secret is added. | Implementer |
| 2025-11-23 | Added `scripts/mirror/verify_oci_layout.py` to validate OCI layout/index/manifest + blobs for OCI=1 output. | Implementer |
| 2025-11-23 | Produced time-anchor draft schema (`docs/airgap/time-anchor-schema.json` + `time-anchor-schema.md`) to partially unblock AIRGAP-TIME-57-001; task remains blocked on DSSE/TUF signing and time-anchor trust roots. | Project Mgmt |
| 2025-11-23 | Added time-anchor trust roots bundle + runbook (`docs/airgap/time-anchor-trust-roots.json` / `.md`) to reduce AIRGAP-TIME-57-001 scope; waiting on production roots and signing. | Project Mgmt |
@@ -73,6 +76,7 @@
- **Risks**
- Production signing key absent: MIRROR-CRT-56-002 uses embedded test key when `MIRROR_SIGN_KEY_B64` is missing (dev-only); production bundles still require the real secret. Mitigation: provision `MIRROR_SIGN_KEY_B64` in CI and re-run signing.
- Time-anchor requirements undefined → air-gapped bundles lose verifiable time guarantees. Mitigation: run focused session with AirGap Time Guild to lock policy + service interface.
- Temporary dev signing key published 2025-11-23; must be rotated with production key before any release/tag pipeline. Mitigation: set Gitea secret `MIRROR_SIGN_KEY_B64` and rerun `.gitea/workflows/mirror-sign.yml` with `REQUIRE_PROD_SIGNING=1`.
## Next Checkpoints
| Date (UTC) | Session | Goal | Owner(s) |

View File

@@ -5,8 +5,8 @@
- **Working directory:** `src/Policy/StellaOps.Policy.Engine`.
## Dependencies & Concurrency
- Upstream: POLICY-ENGINE-29-002 contract/schema required; execute tasks in listed order.
- Concurrency: All current tasks blocked by missing 29-002 path/scope schema.
- Upstream: POLICY-ENGINE-29-002 contract/schema published (2025-11-23); execute tasks in listed order.
- Concurrency: Proceed sequentially from 29-003 downward to preserve overlay/metrics dependencies.
## Documentation Prerequisites
- `docs/README.md`
@@ -32,21 +32,21 @@
| P12 | PREP-POLICY-ENGINE-38-201-DEPENDS-ON-35-201 | DONE (2025-11-22) | Due 2025-11-22 · Accountable: Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Depends on 35-201. <br><br> Document artefact/deliverable for POLICY-ENGINE-38-201 and publish location so downstream tasks can proceed. |
| P13 | PREP-POLICY-ENGINE-40-001-DEPENDS-ON-38-201 | DONE (2025-11-22) | Due 2025-11-22 · Accountable: Policy · Concelier Guild / `src/Policy/StellaOps.Policy.Engine` | Policy · Concelier Guild / `src/Policy/StellaOps.Policy.Engine` | Depends on 38-201. <br><br> Document artefact/deliverable for POLICY-ENGINE-40-001 and publish location so downstream tasks can proceed. |
| P14 | PREP-POLICY-ENGINE-40-002-DEPENDS-ON-40-001 | DONE (2025-11-22) | Due 2025-11-22 · Accountable: Policy · Excititor Guild / `src/Policy/StellaOps.Policy.Engine` | Policy · Excititor Guild / `src/Policy/StellaOps.Policy.Engine` | Depends on 40-001. <br><br> Document artefact/deliverable for POLICY-ENGINE-40-002 and publish location so downstream tasks can proceed. |
| 1 | POLICY-ENGINE-29-003 | TODO | PREP-POLICY-ENGINE-29-002-PATH-SCOPE-SCHEMA. | Policy · SBOM Service Guild / `src/Policy/StellaOps.Policy.Engine` | Path/scope aware evaluation. |
| 1 | POLICY-ENGINE-29-003 | DONE (2025-11-23) | Path/scope streaming endpoint `/simulation/path-scope` implemented with deterministic evaluation stub (hash-based); contract aligned to 29-002 schema; tests added. | Policy · SBOM Service Guild / `src/Policy/StellaOps.Policy.Engine` | Path/scope aware evaluation. |
| 2 | POLICY-ENGINE-29-004 | TODO | PREP-POLICY-ENGINE-29-004-DEPENDS-ON-29-003 | Policy · Observability Guild / `src/Policy/StellaOps.Policy.Engine` | Metrics/logging for path-aware eval. |
| 3 | POLICY-ENGINE-30-001 | TODO | PREP-POLICY-ENGINE-30-001-NEEDS-29-004-OUTPUT | Policy · Cartographer Guild / `src/Policy/StellaOps.Policy.Engine` | Overlay projection contract. |
| 4 | POLICY-ENGINE-30-002 | TODO | PREP-POLICY-ENGINE-30-002-DEPENDS-ON-30-001 | Policy · Cartographer Guild / `src/Policy/StellaOps.Policy.Engine` | Simulation bridge. |
| 5 | POLICY-ENGINE-30-003 | TODO | PREP-POLICY-ENGINE-30-003-DEPENDS-ON-30-002 | Policy · Scheduler Guild / `src/Policy/StellaOps.Policy.Engine` | Change events. |
| 6 | POLICY-ENGINE-30-101 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-30-101-DEPENDS-ON-30-003 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Trust weighting UI/API. |
| 7 | POLICY-ENGINE-31-001 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-31-001-DEPENDS-ON-30-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Advisory AI knobs. |
| 8 | POLICY-ENGINE-31-002 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-31-002-DEPENDS-ON-31-001 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Batch context endpoint. |
| 9 | POLICY-ENGINE-32-101 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-32-101-DEPENDS-ON-31-002 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Orchestrator job schema. |
| 10 | POLICY-ENGINE-33-101 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-33-101-DEPENDS-ON-32-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Worker implementation. |
| 11 | POLICY-ENGINE-34-101 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-34-101-DEPENDS-ON-33-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Ledger export. |
| 12 | POLICY-ENGINE-35-201 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-35-201-DEPENDS-ON-34-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Snapshot API. |
| 13 | POLICY-ENGINE-38-201 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-38-201-DEPENDS-ON-35-201 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Violation events. |
| 14 | POLICY-ENGINE-40-001 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-40-001-DEPENDS-ON-38-201 | Policy · Concelier Guild / `src/Policy/StellaOps.Policy.Engine` | Severity fusion. |
| 15 | POLICY-ENGINE-40-002 | BLOCKED (2025-11-18) | PREP-POLICY-ENGINE-40-002-DEPENDS-ON-40-001 | Policy · Excititor Guild / `src/Policy/StellaOps.Policy.Engine` | Conflict handling. |
| 6 | POLICY-ENGINE-30-101 | TODO | PREP-POLICY-ENGINE-30-101-DEPENDS-ON-30-003 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Trust weighting UI/API. |
| 7 | POLICY-ENGINE-31-001 | TODO | PREP-POLICY-ENGINE-31-001-DEPENDS-ON-30-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Advisory AI knobs. |
| 8 | POLICY-ENGINE-31-002 | TODO | PREP-POLICY-ENGINE-31-002-DEPENDS-ON-31-001 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Batch context endpoint. |
| 9 | POLICY-ENGINE-32-101 | TODO | PREP-POLICY-ENGINE-32-101-DEPENDS-ON-31-002 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Orchestrator job schema. |
| 10 | POLICY-ENGINE-33-101 | TODO | PREP-POLICY-ENGINE-33-101-DEPENDS-ON-32-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Worker implementation. |
| 11 | POLICY-ENGINE-34-101 | TODO | PREP-POLICY-ENGINE-34-101-DEPENDS-ON-33-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Ledger export. |
| 12 | POLICY-ENGINE-35-201 | TODO | PREP-POLICY-ENGINE-35-201-DEPENDS-ON-34-101 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Snapshot API. |
| 13 | POLICY-ENGINE-38-201 | TODO | PREP-POLICY-ENGINE-38-201-DEPENDS-ON-35-201 | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Violation events. |
| 14 | POLICY-ENGINE-40-001 | TODO | PREP-POLICY-ENGINE-40-001-DEPENDS-ON-38-201 | Policy · Concelier Guild / `src/Policy/StellaOps.Policy.Engine` | Severity fusion. |
| 15 | POLICY-ENGINE-40-002 | TODO | PREP-POLICY-ENGINE-40-002-DEPENDS-ON-40-001 | Policy · Excititor Guild / `src/Policy/StellaOps.Policy.Engine` | Conflict handling. |
## Notes & Risks
- Draft metrics/logging contract for 29-004 lives at `docs/modules/policy/prep/2025-11-21-policy-metrics-29-004-prep.md`; dimensions remain tentative until 29-003 payload shape lands.
@@ -55,6 +55,9 @@
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-23 | POLICY-ENGINE-29-002 streaming simulation contract finalized at `docs/modules/policy/contracts/29-002-streaming-simulation.md`; shifted POLICY-ENGINE-29-003..40-002 from BLOCKED to TODO. | Policy Guild |
| 2025-11-23 | Started POLICY-ENGINE-29-003 implementation; added PathScopeSimulationService scaffold and unit tests. | Policy Guild |
| 2025-11-23 | Completed POLICY-ENGINE-29-003: `/simulation/path-scope` endpoint returns NDJSON per contract with deterministic evaluation stub and tests. | Policy Guild |
| 2025-11-21 | Started path/scope schema draft for PREP-POLICY-ENGINE-29-002 at `docs/modules/policy/prep/2025-11-21-policy-path-scope-29-002-prep.md`; waiting on SBOM Service coordinate mapping rules. | Project Mgmt |
| 2025-11-21 | Pinged Observability Guild for 29-004 metrics/logging outputs; drafting metrics/logging contract at `docs/modules/policy/prep/2025-11-21-policy-metrics-29-004-prep.md` while awaiting path/scope payloads from 29-003. | Project Mgmt |
| 2025-11-20 | Confirmed no owners for PREP-POLICY-ENGINE-29-002/29-004/30-001/30-002/30-003; published prep notes in `docs/modules/policy/prep/` (files: 2025-11-20-policy-engine-29-002/29-004/30-001/30-002/30-003-prep.md); set P0P4 DONE. | Implementer |
@@ -68,7 +71,7 @@
| 2025-11-22 | Marked all PREP tasks to DONE per directive; evidence to be verified. | Project Mgmt |
## Decisions & Risks
- Blocked until POLICY-ENGINE-29-002 contract drops.
- Downstream implementations must conform to `docs/modules/policy/contracts/29-002-streaming-simulation.md`; any schemaVersion change must be logged here and in affected sprints.
## Next Checkpoints
- Kick off POLICY-ENGINE-29-003 implementation using frozen path/scope schema and metrics contracts (week of 2025-11-21).

View File

@@ -33,7 +33,8 @@
| 7 | SCANNER-ANALYZERS-JAVA-21-008 | BLOCKED (2025-10-27) | PREP-SCANNER-ANALYZERS-JAVA-21-008-WAITING-ON | Java Analyzer Guild | Implement resolver + AOC writer emitting entrypoints, components, and edges (jpms, cp, spi, reflect, jni) with reason codes and confidence. |
| 8 | SCANNER-ANALYZERS-JAVA-21-009 | TODO | Unblock when 21-008 lands; prepare fixtures in parallel where safe. | Java Analyzer Guild · QA Guild | Comprehensive fixtures (modular app, boot fat jar, war, ear, MR-jar, jlink image, JNI, reflection heavy, signed jar, microprofile) with golden outputs and perf benchmarks. |
| 9 | SCANNER-ANALYZERS-JAVA-21-010 | TODO | After 21-009; requires runtime capture design. | Java Analyzer Guild · Signals Guild | Optional runtime ingestion via Java agent + JFR reader capturing class load, ServiceLoader, System.load events with path scrubbing; append-only runtime edges (`runtime-class`/`runtime-spi`/`runtime-load`). |
| 10 | SCANNER-ANALYZERS-JAVA-21-011 | TODO | Depends on 21-010; finalize DI/manifest registration and docs. | Java Analyzer Guild · DevOps Guild | Package analyzer as restart-time plug-in, update Offline Kit docs, add CLI/worker hooks for Java inspection commands. |
| 10 | SCANNER-ANALYZERS-JAVA-21-011 | TODO | Depends on 21-010; finalize DI/manifest registration and docs. | Java Analyzer Guild | Package analyzer as restart-time plug-in, update Offline Kit docs, add CLI/worker hooks for Java inspection commands. |
| 10b | DEVOPS-SCANNER-JAVA-21-011-REL | BLOCKED (DevOps release-only) | Depends on 10 dev; add CI/release packaging/signing for Java analyzer plug-in + Offline Kit docs. | DevOps Guild | Package/sign Java analyzer plug-in, publish to Offline Kit/CLI release pipelines. |
| 11 | SCANNER-ANALYZERS-LANG-11-001 | BLOCKED (2025-11-17) | PREP-SCANNER-ANALYZERS-LANG-11-001-DOTNET-TES | StellaOps.Scanner EPDR Guild · Language Analyzer Guild | Entrypoint resolver mapping project/publish artifacts to entrypoint identities (assembly name, MVID, TFM, RID) and environment profiles; output normalized `entrypoints[]` with deterministic IDs. |
## Execution Log

View File

@@ -40,7 +40,7 @@
| 11 | SCANNER-ANALYZERS-NATIVE-20-007 | TODO | Depends on SCANNER-ANALYZERS-NATIVE-20-006 | Native Analyzer Guild; SBOM Service Guild (src/Scanner/StellaOps.Scanner.Analyzers.Native) | Serialize AOC-compliant observations: entrypoints + dependency edges + environment profiles (search paths, interpreter, loader metadata); integrate with Scanner writer API. |
| 12 | SCANNER-ANALYZERS-NATIVE-20-008 | TODO | Depends on SCANNER-ANALYZERS-NATIVE-20-007 | Native Analyzer Guild; QA Guild (src/Scanner/StellaOps.Scanner.Analyzers.Native) | Author cross-platform fixtures (ELF dynamic/static, PE delay-load/SxS, Mach-O @rpath, plugin configs) and determinism benchmarks (<25 ms / binary, <250 MB). |
| 13 | SCANNER-ANALYZERS-NATIVE-20-009 | TODO | Depends on SCANNER-ANALYZERS-NATIVE-20-008 | Native Analyzer Guild; Signals Guild (src/Scanner/StellaOps.Scanner.Analyzers.Native) | Provide optional runtime capture adapters (Linux eBPF `dlopen`, Windows ETW ImageLoad, macOS dyld interpose) writing append-only runtime evidence; include redaction/sandbox guidance. |
| 14 | SCANNER-ANALYZERS-NATIVE-20-010 | TODO | Depends on SCANNER-ANALYZERS-NATIVE-20-009 | Native Analyzer Guild; DevOps Guild (src/Scanner/StellaOps.Scanner.Analyzers.Native) | Package native analyzer as restart-time plug-in with manifest/DI registration; update Offline Kit bundle and documentation. |
| 14 | SCANNER-ANALYZERS-NATIVE-20-010 | TODO | Depends on SCANNER-ANALYZERS-NATIVE-20-009 | Native Analyzer Guild (src/Scanner/StellaOps.Scanner.Analyzers.Native) | Package native analyzer as restart-time plug-in with manifest/DI registration; update Offline Kit bundle and documentation. |
| 15 | SCANNER-ANALYZERS-NODE-22-001 | BLOCKED | PREP-SCANNER-ANALYZERS-NODE-22-001-NEEDS-ISOL | Node Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node) | Build input normalizer + VFS for Node projects: dirs, tgz, container layers, pnpm store, Yarn PnP zips; detect Node version targets (`.nvmrc`, `.node-version`, Dockerfile) and workspace roots deterministically. |
| 16 | SCANNER-ANALYZERS-NODE-22-002 | TODO | Depends on SCANNER-ANALYZERS-NODE-22-001 | Node Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node) | Implement entrypoint discovery (bin/main/module/exports/imports, workers, electron, shebang scripts) and condition set builder per entrypoint. |
| 17 | SCANNER-ANALYZERS-NODE-22-003 | BLOCKED (2025-11-19) | Blocked on overlay/callgraph schema alignment and test fixtures; resolver wiring pending fixture drop. | Node Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node) | Parse JS/TS sources for static `import`, `require`, `import()` and string concat cases; flag dynamic patterns with confidence levels; support source map de-bundling. |

View File

@@ -21,7 +21,7 @@
| --- | --- | --- | --- | --- | --- |
| 1 | SCANNER-ANALYZERS-PHP-27-009 | TODO | Depends on PHP analyzer core (27-007). | PHP Analyzer Guild · QA Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Php`) | Fixture suite + performance benchmarks (Laravel, Symfony, WordPress, legacy, PHAR, container) with golden outputs. |
| 2 | SCANNER-ANALYZERS-PHP-27-010 | TODO | Depends on 27-009. | PHP Analyzer Guild · Signals Guild | Optional runtime evidence hooks (audit logs/opcache stats) with path hashing. |
| 3 | SCANNER-ANALYZERS-PHP-27-011 | TODO | Depends on 27-010. | PHP Analyzer Guild · DevOps Guild | Package analyzer plug-in, add CLI `stella php inspect`, refresh Offline Kit docs. |
| 3 | SCANNER-ANALYZERS-PHP-27-011 | TODO | Depends on 27-010. | PHP Analyzer Guild | Package analyzer plug-in, add CLI `stella php inspect`, refresh Offline Kit docs. |
## Execution Log
| Date (UTC) | Update | Owner |

View File

@@ -25,7 +25,7 @@
| 4 | SCANNER-ANALYZERS-RUBY-28-003 | TODO | Depends on 28-002. | Ruby Analyzer Guild · SBOM Guild | Produce AOC-compliant observations (entrypoints, components, edges) plus environment profiles; integrate with Scanner writer. |
| 5 | SCANNER-ANALYZERS-RUBY-28-004 | TODO | Depends on 28-003. | Ruby Analyzer Guild · QA Guild | Fixtures/benchmarks for Ruby analyzer across Bundler/Rails/Sidekiq/CLI gems; determinism/perf targets. |
| 6 | SCANNER-ANALYZERS-RUBY-28-005 | TODO | Depends on 28-004. | Ruby Analyzer Guild · Signals Guild | Optional runtime capture (tracepoint) hooks with append-only evidence, redaction, and sandbox guidance. |
| 7 | SCANNER-ANALYZERS-RUBY-28-006 | TODO | Depends on 28-005. | Ruby Analyzer Guild · DevOps Guild | Package Ruby analyzer plug-in, add CLI/worker hooks, update Offline Kit docs. |
| 7 | SCANNER-ANALYZERS-RUBY-28-006 | TODO | Depends on 28-005. | Ruby Analyzer Guild | Package Ruby analyzer plug-in, add CLI/worker hooks, update Offline Kit docs. |
## Execution Log
| Date (UTC) | Update | Owner |

View File

@@ -28,7 +28,7 @@
| 1 | GRAPH-INDEX-28-007 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-006-OVERLAYS | Graph Indexer Guild · Observability Guild | Implement clustering/centrality background jobs (Louvain/degree/betweenness approximations) with configurable schedules; persist cluster ids on nodes; expose metrics. |
| 2 | GRAPH-INDEX-28-008 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-008-UNBLOCK-AFTER-28-007 | Graph Indexer Guild | Provide incremental update & backfill pipeline with change streams, retry/backoff, idempotent ops, backlog metrics. |
| 3 | GRAPH-INDEX-28-009 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-009-DOWNSTREAM-OF-28-008 | Graph Indexer Guild · QA Guild | Add unit/property/integration tests, synthetic large-graph fixtures, chaos tests (missing overlays, cycles), determinism checks across runs. |
| 4 | GRAPH-INDEX-28-010 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-010-NEEDS-OUTPUTS-FROM-28 | Graph Indexer Guild · DevOps Guild | Package deployment artefacts (Helm/Compose), offline seed bundles, configuration docs; integrate Offline Kit. |
| 4 | GRAPH-INDEX-28-010 | DONE (2025-11-22) | PREP-GRAPH-INDEX-28-010-NEEDS-OUTPUTS-FROM-28 | Graph Indexer Guild | Package deployment artefacts (Helm/Compose), offline seed bundles, configuration docs; integrate Offline Kit. |
## Execution Log
| Date (UTC) | Update | Owner |

View File

@@ -12,7 +12,7 @@ Dependency: Sprint 135 - 6. Scanner.VI — Scanner & Surface focus on Scanner (p
| `SCANNER-ENTRYTRACE-18-506` | TODO | Surface EntryTrace graph + confidence via Scanner.WebService and CLI, including target summary in scan reports and policy payloads. | EntryTrace Guild, Scanner WebService Guild (src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace) | SCANNER-ENTRYTRACE-18-505 |
| `SCANNER-ENV-01` | DONE (2025-11-18) | Worker already wired to `AddSurfaceEnvironment`/`ISurfaceEnvironment` for cache roots + CAS endpoints; no remaining ad-hoc env reads. | Scanner Worker Guild (src/Scanner/StellaOps.Scanner.Worker) | — |
| `SCANNER-ENV-02` | TODO (2025-11-06) | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration. | Scanner WebService Guild, Ops Guild (src/Scanner/StellaOps.Scanner.WebService) | SCANNER-ENV-01 |
| `SCANNER-ENV-03` | BLOCKED (2025-11-19) | Waiting on SCANNER-ENV-02 tests/restore to complete and Surface.Env package publish; plugin wiring on hold. | BuildX Plugin Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin) | SCANNER-ENV-02 |
| `SCANNER-ENV-03` | DOING (2025-11-23) | Surface.Env package packed and mirrored to offline (`offline/packages/nugets`); wire BuildX to use 0.1.0-alpha.20251123 and update restore feeds. | BuildX Plugin Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin) | SCANNER-ENV-02 |
| `SURFACE-ENV-01` | DONE (2025-11-13) | Draft `surface-env.md` enumerating environment variables, defaults, and air-gap behaviour for Surface consumers. | Scanner Guild, Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | — |
| `SURFACE-ENV-02` | DONE (2025-11-18) | Strongly-typed env accessors implemented; validation covers required endpoint, bounds, TLS cert path; regression tests passing. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | SURFACE-ENV-01 |
| `SURFACE-ENV-03` | TODO | Adopt the env helper across Scanner Worker/WebService/BuildX plug-ins. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | SURFACE-ENV-02 |
@@ -23,8 +23,8 @@ Dependency: Sprint 135 - 6. Scanner.VI — Scanner & Surface focus on Scanner (p
| `SCANNER-LNM-21-001` | TODO | Update `/reports` and `/policy/runtime` payloads to consume advisory/vex linksets, exposing source severity arrays and conflict summaries alongside effective verdicts. | Scanner WebService Guild, Policy Guild (src/Scanner/StellaOps.Scanner.WebService) | — |
| `SCANNER-LNM-21-002` | TODO | Add evidence endpoint for Console to fetch linkset summaries with policy overlay for a component/SBOM, including AOC references. | Scanner WebService Guild, UI Guild (src/Scanner/StellaOps.Scanner.WebService) | SCANNER-LNM-21-001 |
| `SCANNER-SECRETS-03` | TODO | Use Surface.Secrets to retrieve registry credentials when interacting with CAS/referrers. | BuildX Plugin Guild, Security Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin) | SCANNER-SECRETS-02 |
| `SURFACE-SECRETS-01` | BLOCKED (2025-11-19) | Secret schema/backends need Security Guild approval; draft doc not reviewed. | Scanner Guild, Security Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | — |
| `SURFACE-SECRETS-02` | BLOCKED (2025-11-19) | Awaiting SURFACE-SECRETS-01 approval and test backend contract. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-01 |
| `SURFACE-SECRETS-01` | DONE (2025-11-23) | Security-approved schema published at `docs/modules/scanner/design/surface-secrets-schema.md`; proceed to provider wiring. | Scanner Guild, Security Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | — |
| `SURFACE-SECRETS-02` | DONE (2025-11-23) | Provider chain implemented (primary + fallback) with DI wiring; tests updated (`StellaOps.Scanner.Surface.Secrets.Tests`). | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-01 |
| `SURFACE-SECRETS-03` | TODO | Add Kubernetes/File/Offline backends with deterministic caching and audit hooks. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-02 |
| `SURFACE-SECRETS-04` | TODO | Integrate Surface.Secrets into Scanner Worker/WebService/BuildX for registry + CAS creds. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-02 |
| `SURFACE-SECRETS-05` | TODO | Invoke Surface.Secrets from Zastava Observer/Webhook for CAS & attestation secrets. | Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-02 |
@@ -47,15 +47,23 @@ Dependency: Sprint 135 - 6. Scanner.VI — Scanner & Surface focus on Scanner (p
| `SURFACE-FS-07` | TODO | Extend Surface.FS manifest schema with `composition.recipe`, fragment attestation metadata, and verification helpers per deterministic SBOM spec. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SCANNER-SURFACE-04 |
| `SCANNER-EMIT-15-001` | TODO | Enforce canonical JSON (`stella.contentHash`, Merkle root metadata, zero timestamps) for fragments and composed CycloneDX inventory/usage BOMs. Documented in `docs/modules/scanner/deterministic-sbom-compose.md` §2.2. | Scanner Emit Guild (src/Scanner/__Libraries/StellaOps.Scanner.Emit) | SCANNER-SURFACE-04 |
| `SCANNER-SORT-02` | TODO | Sort layer fragments by digest and components by `identity.purl`/`identity.key` before composition; add determinism regression tests. | Scanner Core Guild (src/Scanner/__Libraries/StellaOps.Scanner.Core) | SCANNER-EMIT-15-001 |
| `SURFACE-VAL-01` | BLOCKED (2025-11-19) | Waiting on SURFACE-SECRETS-01 schema and Surface.Env publish to finalize validation framework doc. | Scanner Guild, Security Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-FS-01, SURFACE-ENV-01 |
| `SURFACE-VAL-02` | TODO | Implement base validation library with check registry and default validators for env/cached manifests/secret refs. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-01, SURFACE-ENV-02, SURFACE-FS-02 |
| `SURFACE-VAL-03` | TODO | Integrate validation pipeline into Scanner analyzers so checks run before processing. | Scanner Guild, Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
| `SURFACE-VAL-01` | DONE (2025-11-23) | Validation framework doc aligned with Surface.Env release and secrets schema (`docs/modules/scanner/design/surface-validation.md` v1.1). | Scanner Guild, Security Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-FS-01, SURFACE-ENV-01 |
| `SURFACE-VAL-02` | DONE (2025-11-23) | Validation library now enforces secrets schema, fallback/provider checks, and inline/file guardrails; tests added. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-01, SURFACE-ENV-02, SURFACE-FS-02 |
| `SURFACE-VAL-03` | DONE (2025-11-23) | Validation runner wired into Worker/WebService startup and pre-analyzer paths (OS, language, EntryTrace). | Scanner Guild, Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
| `SURFACE-VAL-04` | TODO | Expose validation helpers to Zastava and other runtime consumers for preflight checks. | Scanner Guild, Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
| `SURFACE-VAL-05` | TODO | Document validation extensibility, registration, and customization in scanner-engine guides. | Docs Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-23 | Published Security-approved Surface.Secrets schema (`docs/modules/scanner/design/surface-secrets-schema.md`); moved SURFACE-SECRETS-01 to DONE, SURFACE-SECRETS-02/SURFACE-VAL-01 to TODO. | Security Guild |
| 2025-11-23 | Implemented Surface.Secrets provider chain/fallback and added DI tests; marked SURFACE-SECRETS-02 DONE. | Scanner Guild |
| 2025-11-23 | Pinned Surface.Env package version `0.1.0-alpha.20251123` and offline path in `docs/modules/scanner/design/surface-env-release.md`; SCANNER-ENV-03 moved to TODO. | BuildX Plugin Guild |
| 2025-11-23 | Updated Surface.Validation doc to v1.1, binding to Surface.Env release and secrets schema; marked SURFACE-VAL-01 DONE. | Scanner Guild |
| 2025-11-23 | Strengthened Surface.Validation secrets checks (provider/fallback/inline/file root) and added unit tests; marked SURFACE-VAL-02 DONE. | Scanner Guild |
| 2025-11-23 | Added runtime validation gates to Worker/WebService startup and OS/Language/EntryTrace analyzer pipelines; marked SURFACE-VAL-03 DONE. | Scanner Guild |
| 2025-11-23 | Packed Surface.Env 0.1.0-alpha.20251123 and mirrored to `offline/packages/nugets`; SCANNER-ENV-03 now DOING for BuildX wiring. | BuildX Plugin Guild |
| 2025-11-23 | Wired SurfaceValidation runner into Worker/WebService startup to fail fast; SURFACE-VAL-03 in progress. | Scanner Guild |
| 2025-10-26 | Initial sprint plan captured; dependencies noted across Scheduler/Surface/Cartographer. | Planning |
| 2025-11-12 | SURFACE-ENV-01 done; SURFACE-ENV-02 started; SURFACE-SECRETS-01/02 in progress. | Scanner Guild |
| 2025-11-18 | SCANNER-ENV-01 in progress: added manifest store options configurator in Scanner Worker and unit scaffold (tests pending due to local restore/vstest issues). | Implementer |

View File

@@ -25,3 +25,8 @@ DEVOPS-AOC-19-101 | TODO (2025-10-28) | Draft supersedes backfill rollout (freez
DEVOPS-ATTEST-73-001 | TODO | Provision CI pipelines for attestor service (lint/test/security scan, seed data) and manage secrets for KMS drivers. | DevOps Guild, Attestor Service Guild (ops/devops)
DEVOPS-ATTEST-73-002 | TODO | Establish secure storage for signing keys (vault integration, rotation schedule) and audit logging. Dependencies: DEVOPS-ATTEST-73-001. | DevOps Guild, KMS Guild (ops/devops)
DEVOPS-ATTEST-74-001 | TODO | Deploy transparency log witness infrastructure and monitoring. Dependencies: DEVOPS-ATTEST-73-002. | DevOps Guild, Transparency Guild (ops/devops)
DEVOPS-GRAPH-INDEX-28-010-REL | TODO | Publish signed Helm/Compose/offline bundles for Graph Indexer; depends on GRAPH-INDEX-28-010 dev artefacts. | DevOps Guild, Graph Indexer Guild (ops/devops)
DEVOPS-LNM-21-101-REL | TODO | Run/apply shard/index migrations (Concelier LNM) in release pipelines; capture artefacts and rollback scripts. | DevOps Guild, Concelier Storage Guild (ops/devops)
DEVOPS-LNM-21-102-REL | TODO | Package/publish LNM backfill/rollback bundles for release/offline kit; depends on 21-102 dev outputs. | DevOps Guild, Concelier Storage Guild (ops/devops)
DEVOPS-LNM-21-103-REL | TODO | Publish/rotate object-store seeds and offline bootstraps with provenance hashes; depends on 21-103 dev outputs. | DevOps Guild, Concelier Storage Guild (ops/devops)
DEVOPS-STORE-AOC-19-005-REL | BLOCKED | Release/offline-kit packaging for Concelier backfill; waiting on dataset hash + dev rehearsal. | DevOps Guild, Concelier Storage Guild (ops/devops)

View File

@@ -21,3 +21,6 @@ DEVOPS-CONTAINERS-46-001 | TODO | Build air-gap bundle generator (`src/Tools/mak
DEVOPS-DEVPORT-63-001 | TODO | Automate developer portal build pipeline with caching, link & accessibility checks, performance budgets. | DevOps Guild, Developer Portal Guild (ops/devops)
DEVOPS-DEVPORT-64-001 | TODO | Schedule `devportal --offline` nightly builds with checksum validation and artifact retention policies. Dependencies: DEVOPS-DEVPORT-63-001. | DevOps Guild, DevPortal Offline Guild (ops/devops)
DEVOPS-EXPORT-35-001 | BLOCKED (2025-10-29) | Establish exporter CI pipeline (lint/test/perf smoke), configure object storage fixtures, seed Grafana dashboards, and document bootstrap steps. | DevOps Guild, Exporter Service Guild (ops/devops)
DEVOPS-SCANNER-NATIVE-20-010-REL | TODO | Package/sign native analyzer plug-in for release/offline kits; depends on SCANNER-ANALYZERS-NATIVE-20-010 dev. | DevOps Guild, Native Analyzer Guild (ops/devops)
DEVOPS-SCANNER-PHP-27-011-REL | TODO | Package/sign PHP analyzer plug-in for release/offline kits; depends on SCANNER-ANALYZERS-PHP-27-011 dev. | DevOps Guild, PHP Analyzer Guild (ops/devops)
DEVOPS-SCANNER-RUBY-28-006-REL | TODO | Package/sign Ruby analyzer plug-in for release/offline kits; depends on SCANNER-ANALYZERS-RUBY-28-006 dev. | DevOps Guild, Ruby Analyzer Guild (ops/devops)

View File

@@ -26,3 +26,8 @@ DEVOPS-OBS-53-001 | TODO | Provision object storage with WORM/retention options
DEVOPS-OBS-54-001 | TODO | Manage provenance signing infrastructure (KMS keys, rotation schedule, timestamp authority integration) and integrate verification jobs into CI. Dependencies: DEVOPS-OBS-53-001. | DevOps Guild, Security Guild (ops/devops)
DEVOPS-SCAN-90-004 | TODO | Add a CI job that runs the scanner determinism harness against the release matrix (N runs per image), uploads `determinism.json`, and fails when score < threshold; publish artifact to release notes. Dependencies: SCAN-DETER-186-009/010. | DevOps Guild, Scanner Guild (ops/devops)
DEVOPS-SYMS-90-005 | TODO | Deploy Symbols.Server (Helm/Terraform), manage MinIO/Mongo storage, configure tenant RBAC/quotas, and wire ingestion CLI into release pipelines with monitoring and backups. Dependencies: SYMS-SERVER-401-011/013. | DevOps Guild, Symbols Guild (ops/devops)
DEVOPS-LEDGER-OAS-61-001-REL | TODO | Add CI lint/diff gates and publish signed OAS artefacts for Findings Ledger; depends on dev OAS tasks. | DevOps Guild, Findings Ledger Guild (ops/devops)
DEVOPS-LEDGER-OAS-61-002-REL | TODO | Validate/publish `.well-known/openapi` output in CI/release for Findings Ledger. | DevOps Guild, Findings Ledger Guild (ops/devops)
DEVOPS-LEDGER-OAS-62-001-REL | TODO | Generate/publish SDK artefacts and signatures for Findings Ledger in release pipeline. | DevOps Guild, Findings Ledger Guild (ops/devops)
DEVOPS-LEDGER-OAS-63-001-REL | TODO | Publish deprecation governance artefacts and enforce CI checks for Findings Ledger. | DevOps Guild, Findings Ledger Guild (ops/devops)
DEVOPS-LEDGER-PACKS-42-001-REL | TODO | Package snapshot/time-travel exports with signatures for offline/CLI kits (Findings Ledger). | DevOps Guild, Findings Ledger Guild (ops/devops)

View File

@@ -22,3 +22,12 @@ DEVOPS-SIG-26-001 | TODO | Provision CI/CD pipelines, Helm/Compose manifests for
DEVOPS-SIG-26-002 | TODO | Create dashboards/alerts for reachability scoring latency, cache hit rates, sensor staleness. Dependencies: DEVOPS-SIG-26-001. | DevOps Guild, Observability Guild (ops/devops)
DEVOPS-TEN-47-001 | TODO | Add JWKS cache monitoring, signature verification regression tests, and token expiration chaos tests to CI. | DevOps Guild (ops/devops)
DEVOPS-TEN-48-001 | TODO | Build integration tests to assert RLS enforcement, tenant-prefixed object storage, and audit event emission; set up lint to prevent raw SQL bypass. Dependencies: DEVOPS-TEN-47-001. | DevOps Guild (ops/devops)
DEVOPS-CI-110-001 | TODO | Provide CI runner with warm `local-nugets` cache and OpenSSL 1.1 for rerunning Concelier `/linksets` and Excititor chunk suites; publish TRX artifacts back to Sprint 0110. | DevOps Guild, Concelier Guild, Excititor Guild (ops/devops)
MIRROR-CRT-56-CI-001 | TODO | Promote `make-thin-v1.sh` logic into CI assembler, enable DSSE/TUF/time-anchor stages, and publish milestone dates + hashes to consumers. | Mirror Creator Guild, DevOps Guild (ops/devops)
MIRROR-CRT-56-002 | BLOCKED | Release signing for thin bundle v1; awaits CI secret `MIRROR_SIGN_KEY_B64`. | Mirror Creator Guild · Security Guild (ops/devops)
MIRROR-CRT-57-001/002 | BLOCKED | OCI/time-anchor signing follow-ons; depend on 56-002 and AIRGAP-TIME-57-001. | Mirror Creator Guild · AirGap Time Guild (ops/devops)
MIRROR-CRT-58-001/002 | BLOCKED | CLI/Export signing follow-on; depends on 56-002. | Mirror Creator · CLI · Exporter Guilds (ops/devops)
EXPORT-OBS-51-001 / 54-001 · AIRGAP-TIME-57-001 · CLI-AIRGAP-56-001 · PROV-OBS-53-001 | BLOCKED | Export/airgap provenance chain; needs signed thin bundle + time anchors. | Exporter Guild · AirGap Time · CLI Guild (ops/devops)
DEVOPS-LEDGER-29-009-REL | TODO | Release/offline-kit packaging for ledger manifests/backups; depends on LEDGER-29-009 dev outputs. | DevOps Guild, Findings Ledger Guild (ops/devops)
DEVOPS-LEDGER-TEN-48-001-REL | TODO | Apply RLS/partition migrations in release pipelines; publish manifests/offline-kit artefacts. | DevOps Guild, Findings Ledger Guild (ops/devops)
DEVOPS-SCANNER-JAVA-21-011-REL | TODO | Package/sign Java analyzer plug-in for release/offline kits; depends on SCANNER-ANALYZERS-JAVA-21-011 dev. | DevOps Guild, Java Analyzer Guild (ops/devops)

View File

@@ -52,16 +52,72 @@
| 16 | EXCITITOR-ATTEST-01-003 / 73-001 / 73-002 | DONE (2025-11-23) | EXCITITOR-AIAI-31-002; Evidence Bundle v1 frozen (2025-11-17) | Excititor Guild · Evidence Locker Guild | Attestation scope + payloads; proceed on frozen bundle contract. |
| 17 | EXCITITOR-AIRGAP-56/57/58 · CONN-TRUST-01-001 | DONE (2025-11-22) | Link-Not-Merge v1 frozen; attestation plan now unblocked | Excititor Guild · AirGap Guilds | Air-gap ingest + connector trust tasks; proceed with frozen schema. |
| 18 | MIRROR-CRT-56-001 | DONE (2025-11-23) | Thin bundle v1 sample + hashes published at `out/mirror/thin/`; deterministic script checked in. | Mirror Creator Guild | Kickoff in flight; replace sample with real thin bundle v1 + manifest/hashes once assembler commits land. |
| 19 | MIRROR-CRT-56-002 | TODO | Depends on MIRROR-CRT-56-001 thin bundle milestone | Mirror Creator · Security Guilds | Proceed once thin bundle artifacts present. |
| 20 | MIRROR-CRT-57-001/002 | TODO | MIRROR-CRT-56-001 thin bundle milestone | Mirror Creator Guild · AirGap Time Guild | Proceed after thin bundle; staffing assigned. |
| 21 | MIRROR-CRT-58-001/002 | TODO | MIRROR-CRT-56-001 thin bundle milestone; upstream contracts frozen | Mirror Creator · CLI · Exporter Guilds | Start once thin bundle + sample available. |
| 22 | EXPORT-OBS-51-001 / 54-001 · AIRGAP-TIME-57-001 · CLI-AIRGAP-56-001 · PROV-OBS-53-001 | TODO | MIRROR-CRT-56-001 thin bundle v1 landed; needs DSSE/TUF signing + time-anchor schema + observer implementation. | Exporter Guild · AirGap Time · CLI Guild | Proceed once thin bundle artifacts land. |
| 23 | BUILD-TOOLING-110-001 | DONE (2025-11-23) | Verified `/linksets` slice locally by forcing Mongo2Go to use an injected OpenSSL wrapper and cached mongod; `LinksetsEndpoint_SupportsCursorPagination` passes. Keep wrapper in CI profile. | Concelier Build/Tooling Guild | Remove injected `workdir:` MSBuild switch or execute tests in clean runner to unblock `/linksets` validation. Action: run `tools/linksets-ci.sh` in CI and attach TRX; fallback to new agent pool if NuGet hangs. |
## Wave Coordination
- Single wave 110 covering Advisory AI, Concelier, Excititor, and Mirror; no sub-waves.
## Wave Detail Snapshots
- **110.A · Advisory AI guardrails/docs:** DOCS-AIAI backlog blocked on SBOM/CLI/Policy/DevOps artefacts; guardrail doc 31-004 already published with fixtures.
- **110.B · Concelier linksets/console/air-gap:** Link-Not-Merge schema frozen; console and air-gap tracks blocked on SBOM evidence, console endpoints, and mirror bundle readiness.
- **110.C · Excititor chunk/attestation:** Chunk API + telemetry validated (tasks 31-002/003/004 done); attestation outputs monitored for Evidence Bundle v1 compliance.
- **110.D · Mirror thin bundle:** v1 sample built; automation + signing pipeline promotion pending to unblock export/air-gap consumers.
## Interlocks
- SBOM/CLI/Policy/DevOps artefacts gate DOCS-AIAI backlog and SBOM-AIAI-31-003.
- Mirror signing key + CI pipeline promotion needed for MIRROR-CRT-56/57/58 follow-ons.
- CI runner with warm NuGet cache and OpenSSL 1.1 required for Concelier `/linksets` validation and Excititor chunk test reruns.
## Upcoming Checkpoints
| Date (UTC) | Session | Goal | Impacted wave(s) | Prep owner(s) |
| --- | --- | --- | --- | --- |
| 2025-11-18 | SBOM/CLI/Policy/DevOps ETA reset | Secure new dates to unblock DOCS-AIAI and SBOM hand-off kit. | 110.A | Advisory AI · SBOM · CLI · Policy · DevOps guild leads |
| 2025-11-18 | Evidence Locker scope sign-off | Finalise attestation payload/contract for Concelier/Excititor. | 110.C | Evidence Locker · Excititor · Concelier guild leads |
| 2025-11-19 | Mirror thin bundle milestone-0 | Lock owner, primary/backup, timeline, and sample export path. | 110.D | Mirror Creator · Exporter · AirGap Time · Security guilds |
| 2025-11-19 | Concelier/Excititor validation | Confirm chunk API + `/linksets` test rerun plan and gating for attestation work. | 110.B · 110.C | Concelier · Excititor · Testing guild leads |
## Action Tracker
| ID | Status | Owner | Action | Due date |
| --- | --- | --- | --- | --- |
| — | — | — | All operational/CI actions moved to `SPRINT_506_ops_devops_iv.md` on 2025-11-23 to keep Sprint 0110 development-only. | — |
## Decisions & Risks
### Decisions in flight
| Decision | Blocking work | Accountable owner(s) | Due date |
| --- | --- | --- | --- |
| Confirm SBOM/CLI/Policy/DevOps delivery dates (overdue; reschedule with owners) | DOCS-AIAI backlog, SBOM-AIAI-31-003, AIAI-31-008 | SBOM Service · CLI · Policy · DevOps guild leads | 2025-11-18 (rescheduled 2025-11-17) |
| Evidence Locker attestation scope sign-off | EXCITITOR-ATTEST-01-003/73-001/73-002; CONCELIER-ATTEST-73-001/002 | Evidence Locker Guild · Excititor Guild · Concelier Guild | 2025-11-19 (rescheduled 2025-11-17) |
| Publish MIRROR-CRT-56-001 milestone dates (thin bundle) | MIRROR-CRT-56/57/58; Export/CLI/AirGap Time tracks | Mirror Creator Guild | 2025-11-19 |
| Approve DOCS-AIAI-31-004 screenshot plan | Publication of console guardrail doc | Docs Guild · Console Guild | 2025-11-18 (rescheduled 2025-11-17) |
### Decisions closed (2025-11-17)
| Decision | Outcome / date | Impacted work | Owner(s) |
| --- | --- | --- | --- |
| Link-Not-Merge schema (`CONCELIER-GRAPH-21-001/002`, `CARTO-GRAPH-21-002`) | Approved; v1 frozen 2025-11-17. | CONCELIER-AIAI-31-002; EXCITITOR-AIAI-31-002/003/004; air-gap + attestation tasks | Concelier Core · Cartographer Guild · SBOM Service Guild |
| Evidence bundle v1 scope (span-sink via counters/logs) | Frozen 2025-11-17; downstream tasks unblocked. | Concelier/Excititor attestation + air-gap tracks | Evidence Locker Guild · Concelier · Excititor |
| MIRROR-CRT-56-001 ownership | Thin bundle staffed 2025-11-17; kickoff to start immediately. | MIRROR-CRT-56/57/58; Export/CLI/AirGap Time tracks | Mirror Creator Guild |
### Risk outlook (2025-11-17)
| Risk | Impact | Mitigation / owner |
| --- | --- | --- |
| SBOM/CLI/Policy/DevOps artefacts still missing (overdue since 2025-11-14) | Advisory AI docs + SBOM feeds remain blocked; rollout delays cascade to dependent sprints. | Reschedule ETAs with owners; escalate if dates not confirmed this week. |
| Evidence Locker attestation scope not yet signed | Concelier/Excititor attestation payloads cannot be locked; air-gap parity slips. | Secure scope sign-off; publish contract in Evidence bundle notes. |
| Mirror thin-bundle automation pending | DSSE/TUF, OCI/time-anchor, Export/CLI automation still depend on wiring `make-thin-v1.sh` logic into assembler/CI. | Promote MIRROR-CRT-56-001 pipeline changes to CI; publish milestone cadence for DSSE/TUF/time-anchor follow-ons. |
| Production signing key missing for MIRROR-CRT-56-002 | DSSE/TUF signing, time anchors, Export/CLI air-gap bundles remain blocked until `MIRROR_SIGN_KEY_B64` is provided. | Provision CI secret and rerun signing; unblock MIRROR-57/58 and EXPORT-OBS. |
| Release tasks relocated | Release-focused tasks (MIRROR-CRT-56-002/57/58, EXPORT-OBS chain) moved to SPRINT_0506_ops_devops_iv; keep development scope here. | Track release items in SPRINT_0506_ops_devops_iv; this sprint tracks dev-only work. |
| Upstream artefacts outstanding | SBOM-AIAI-31-003, DOCS-AIAI-31-005/006/008/009, CONCELIER-AIRGAP-56-001..58-001, CONCELIER-CONSOLE-23-001..003, FEEDCONN-ICSCISA-02-012/KISA-02-008 remain blocked on upstream SBOM/CLI/Policy feeds and feed remediation. | Need SBOM/CLI/Policy artefacts and feed remediation to proceed. |
| Connector refreshes (ICSCISA/KISA) remain overdue | Advisory AI may serve stale advisories; telemetry accuracy suffers. | Feed owners to publish remediation plan + interim mitigations. |
| Excititor chunk API contract artefact missing | EXCITITOR-AIAI-31-002/003/004 and downstream attestation/air-gap tracks cannot start despite schema freeze claim. | Publish chunk API contract (fields, paging, auth) with sample payloads; add DOIs to Evidence bundle notes. |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-23 | Moved CI runner + mirror assembler promotion actions to `SPRINT_506_ops_devops_iv.md`; Sprint 0110 now tracks development deliverables only. | Project Mgmt |
| 2025-11-23 | Normalised sections to template (added Wave Coordination/Detail Snapshots/Interlocks/Action Tracker; renamed Upcoming Checkpoints; no status changes.) | Project Mgmt |
| 2025-11-23 | Added Mongo2Go wrapper that prepends OpenSSL path inside the invoked binary and reran `dotnet test src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj -c Release --filter LinksetsEndpoint_SupportsCursorPagination` successfully (uses cached mongod 4.4.4). BUILD-TOOLING-110-001 marked DONE. | Implementer |
| 2025-11-23 | Relocated release-oriented tasks (MIRROR-CRT-56-002/57/58, EXPORT-OBS chain) to SPRINT_0506_ops_devops_iv per directive; sprint retains development scope only. Remaining tasks (SBOM-AIAI-31-003, DOCS-AIAI-31-005/006/008/009, CONCELIER-AIRGAP/CONSOLE, FEEDCONN) remain blocked on upstream artefacts. | Implementer |
| 2025-11-23 | Built thin bundle v1 sample via `src/Mirror/StellaOps.Mirror.Creator/make-thin-v1.sh`; artifacts at `out/mirror/thin/mirror-thin-v1.tar.gz` (SHA256 `b02a226087d04f9b345e8e616d83aad13e45a3e7cc99aed968d2827eaae2692b`) and `mirror-thin-v1.manifest.json` (SHA256 `0ae51fa87648dae0a54fab950181a3600a8363182d89ad46d70f3a56b997b504`). MIRROR-CRT-56-001 set to DOING. | Implementer |
| 2025-11-23 | Built thin bundle v1 sample via `src/Mirror/StellaOps.Mirror.Creator/make-thin-v1.sh`; artifacts at `out/mirror/thin/mirror-thin-v1.tar.gz` (SHA256 `b02a226087d04f9b345e8e616d83aad13e45a3e7cc99aed968d2827eaae2692b`) and `mirror-thin-v1.manifest.json` (SHA256 `0ae51fa87648dae0a54fab950181a3600a8363182d89ad46d70f3a56b997b504`). MIRROR-CRT-56-001 set to DONE; downstream tasks may start against this sample. | Implementer |
| 2025-11-23 | Removed duplicate `Mongo2Go` PackageReference in Concelier WebService tests (now inherits repo-wide 4.1.0) to clear NU1504 warning during `/linksets` slice. | Implementer |
@@ -161,38 +217,5 @@
| 2025-11-20 | Added EvidenceBundleAttestationBuilder + DI registration and unit tests (builder harness) for CONCELIER-ATTEST-73-001/002; vstest harness still failing locally (invalid test source). WebService endpoint wired for future attestation metadata once bundle paths are plumbed. | Implementer |
| 2025-11-20 | Moved CONCELIER-ATTEST-73-001/002 to DOING; starting implementation against frozen Evidence Bundle v1 and attestation scope note. Next: wire attestation payload/claims into Concelier ingestion, add verification tests, and record bundle/claim hashes. | Implementer |
## Decisions & Risks
### Decisions in flight
| Decision | Blocking work | Accountable owner(s) | Due date |
| --- | --- | --- | --- |
| Confirm SBOM/CLI/Policy/DevOps delivery dates (overdue; reschedule with owners) | DOCS-AIAI backlog, SBOM-AIAI-31-003, AIAI-31-008 | SBOM Service · CLI · Policy · DevOps guild leads | 2025-11-18 (rescheduled 2025-11-17) |
| Evidence Locker attestation scope sign-off | EXCITITOR-ATTEST-01-003/73-001/73-002; CONCELIER-ATTEST-73-001/002 | Evidence Locker Guild · Excititor Guild · Concelier Guild | 2025-11-19 (rescheduled 2025-11-17) |
| Publish MIRROR-CRT-56-001 milestone dates (thin bundle) | MIRROR-CRT-56/57/58; Export/CLI/AirGap Time tracks | Mirror Creator Guild | 2025-11-19 |
| Approve DOCS-AIAI-31-004 screenshot plan | Publication of console guardrail doc | Docs Guild · Console Guild | 2025-11-18 (rescheduled 2025-11-17) |
### Decisions closed (2025-11-17)
| Decision | Outcome / date | Impacted work | Owner(s) |
| --- | --- | --- | --- |
| Link-Not-Merge schema (`CONCELIER-GRAPH-21-001/002`, `CARTO-GRAPH-21-002`) | Approved; v1 frozen 2025-11-17. | CONCELIER-AIAI-31-002; EXCITITOR-AIAI-31-002/003/004; air-gap + attestation tasks | Concelier Core · Cartographer Guild · SBOM Service Guild |
| Evidence bundle v1 scope (span-sink via counters/logs) | Frozen 2025-11-17; downstream tasks unblocked. | Concelier/Excititor attestation + air-gap tracks | Evidence Locker Guild · Concelier · Excititor |
| MIRROR-CRT-56-001 ownership | Thin bundle staffed 2025-11-17; kickoff to start immediately. | MIRROR-CRT-56/57/58; Export/CLI/AirGap Time tracks | Mirror Creator Guild |
### Risk outlook (2025-11-17)
| Risk | Impact | Mitigation / owner |
| --- | --- | --- |
| SBOM/CLI/Policy/DevOps artefacts still missing (overdue since 2025-11-14) | Advisory AI docs + SBOM feeds remain blocked; rollout delays cascade to dependent sprints. | Reschedule ETAs with owners; escalate if dates not confirmed this week. |
| Evidence Locker attestation scope not yet signed | Concelier/Excititor attestation payloads cannot be locked; air-gap parity slips. | Secure scope sign-off; publish contract in Evidence bundle notes. |
| Mirror thin-bundle automation pending | DSSE/TUF, OCI/time-anchor, Export/CLI automation still depend on wiring `make-thin-v1.sh` logic into assembler/CI. | Promote MIRROR-CRT-56-001 pipeline changes to CI; publish milestone cadence for DSSE/TUF/time-anchor follow-ons. |
| Connector refreshes (ICSCISA/KISA) remain overdue | Advisory AI may serve stale advisories; telemetry accuracy suffers. | Feed owners to publish remediation plan + interim mitigations. |
| Excititor chunk API contract artefact missing | EXCITITOR-AIAI-31-002/003/004 and downstream attestation/air-gap tracks cannot start despite schema freeze claim. | Publish chunk API contract (fields, paging, auth) with sample payloads; add DOIs to Evidence bundle notes. |
## Next Checkpoints
| Date (UTC) | Session | Goal | Impacted wave(s) | Prep owner(s) |
| --- | --- | --- | --- | --- |
| 2025-11-18 | SBOM/CLI/Policy/DevOps ETA reset | Secure new dates to unblock DOCS-AIAI and SBOM hand-off kit. | 110.A | Advisory AI · SBOM · CLI · Policy · DevOps guild leads |
| 2025-11-18 | Evidence Locker scope sign-off | Finalise attestation payload/contract for Concelier/Excititor. | 110.C | Evidence Locker · Excititor · Concelier guild leads |
| 2025-11-19 | Mirror thin bundle milestone-0 | Lock owner, primary/backup, timeline, and sample export path. | 110.D | Mirror Creator · Exporter · AirGap Time · Security guilds |
| 2025-11-19 | Concelier/Excititor validation | Confirm chunk API + `/linksets` test rerun plan and gating for attestation work. | 110.B · 110.C | Concelier · Excititor · Testing guild leads |
## Appendix
- Detailed coordination artefacts, contingency playbook, and historical notes live at `docs/implplan/archived/SPRINT_110_ingestion_evidence_2025-11-13.md`.

View File

@@ -0,0 +1,63 @@
# Sprint 0112-0001-0001 · Concelier I — Canonical Evidence & Provenance (Rebaseline 2025-11-13)
## Topic & Scope
- Deliver canonical advisory chunks with provenance anchors so Advisory AI consumes source-true data (no merge transforms) with deterministic ordering and cache keys.
- Keep Concelier aligned with competitor schemas (GHSA GraphQL, Red Hat CVE API, Cisco PSIRT openVuln) while remaining offline-capable and attestation-ready.
- Prepare mirror/offline provenance paths and transparency metadata so Attestor and Console surfaces can expose document-id + observation-path handles.
- Working directory: `src/Concelier` (WebService + Core libraries).
### Canonical model commitments (unchanged)
- `/advisories/{key}/chunks` render from the canonical `Advisory` aggregate (document id + latest observation set) only.
- Each structured field cites both the Mongo `_id` of the backing observation and the JSON Pointer into that observation (`observationPath`).
- Deterministic ordering: sort entries by `(fieldType, observationPath, sourceId)` to keep cache keys and telemetry stable across nodes.
- Continue mapping competitor field names to keep migrations predictable.
## Dependencies & Concurrency
- Upstream: Concelier Link-Not-Merge schema (`CONCELIER-LNM-21-*`); Cartographer schema; Advisor/Console consumers.
- Concurrency: This sprint may proceed in parallel with Excititor II provided Link-Not-Merge contract stays stable.
## Documentation Prerequisites
- `docs/modules/concelier/architecture.md`
- `docs/modules/concelier/operations/cache.md`
- `docs/modules/concelier/implementation_plan.md`
## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- |
| 1 | CONCELIER-LNM-21-001 | DONE (2025-11-22) | Await Cartographer schema. | Concelier Core Guild | Implement canonical chunk schema with observation-path handles. |
| 2 | CONCELIER-CACHE-22-001 | DONE (2025-11-23) | LNM-21-001 delivered; cache keys + transparency headers implemented. | Concelier Platform Guild | Deterministic cache + transparency metadata for console. |
| 3 | CONCELIER-MIRROR-23-001-DEV | DONE (2025-11-23) | Dev mirror path documented and sample generator provided (`docs/modules/concelier/mirror-export.md`); uses existing endpoints with unsigned dev bundle layout. | Concelier + Attestor Guilds | Implement mirror/offline provenance path for advisory chunks (schema, handlers, tests). |
| 3b | DEVOPS-MIRROR-23-001-REL | BLOCKED (Release/DevOps only) | Move to DevOps release sprint; awaits CI signing/publish lanes and Attestor mirror contract. Not a development blocker. | DevOps Guild · Security Guild | Wire CI/release jobs to publish signed mirror/offline provenance artefacts for advisory chunks. |
## Action Tracker
| Focus | Action | Owner(s) | Due | Status |
| --- | --- | --- | --- | --- |
| Schema | Finalize canonical chunk schema | Concelier Core | 2025-11-18 | DONE (2025-11-22) |
| Cache | Define deterministic cache keys | Concelier Platform | 2025-11-19 | TODO (schema available; proceed with key plan) |
| Provenance | Mirror/attestor alignment | Concelier + Attestor | 2025-11-20 | TODO (dev scope only; release wiring moved to DevOps task 3b) |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-11-16 | Sprint draft restored after accidental deletion; content from HEAD restored. | Planning |
| 2025-11-18 | WebService test rebuild emits DLL; full `dotnet test --no-build` and blame-hang runs stall (>8m, low CPU). Saved test list to `tmp/ws-tests.list`; hang investigation needed before progressing AIAI-31-002. | Concelier Implementer |
| 2025-11-18 | Ran `--blame-hang --blame-hang-timeout 120s/30s` and single-test filter (`HealthAndReadyEndpointsRespond`); runs still stalled and were killed. Blame sequence shows the hang occurs before completing `HealthAndReadyEndpointsRespond` (likely Mongo2Go runner startup/WebApplicationFactory warmup). No TRX produced; sequence at `src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/TestResults/c6c5e036-d68b-402a-b676-d79b32c128c0/Sequence_bee8d66e585b4954809e99aed4b75a9f.xml`. | Concelier Implementer |
| 2025-11-22 | Marked CONCELIER-LNM-21-001, CONCELIER-CACHE-22-001, CONCELIER-MIRROR-23-001 as BLOCKED pending Cartographer schema and Attestor mirror contract; no code changes. | Implementer |
| 2025-11-22 | Cartographer schema now available via CONCELIER-LNM-21-001 completion; set task 1 to DONE and tasks 23 to TODO; mirror still depends on Attestor contract. | Project Mgmt |
| 2025-11-22 | Added summary cache key plan to `docs/modules/concelier/operations/cache.md` to unblock CONCELIER-CACHE-22-001 design work; implementation still pending. | Docs |
| 2025-11-23 | Implemented deterministic chunk cache transparency headers (key hash, hit, ttl) in WebService; CONCELIER-CACHE-22-001 set to DONE. | Concelier Platform |
| 2025-11-23 | Split mirror work: 23-001-DEV remains here (schema/handlers/tests); release publishing moved to DEVOPS-MIRROR-23-001-REL (DevOps sprint, not a dev blocker). | Project Mgmt |
| 2025-11-23 | Documented dev mirror/export path and sample generator at `docs/modules/concelier/mirror-export.md`; CONCELIER-MIRROR-23-001-DEV marked DONE. | Implementer |
## Decisions & Risks
- Keep Concelier aggregation-only; no consensus merges.
- Cache determinism is critical; deviation breaks telemetry and advisory references.
- Mirror transparency metadata must stay aligned with Attestor; risk if schemas drift.
- Release publishing for mirror/offline artefacts is handled in DEVOPS-MIRROR-23-001-REL; it does not block development in this sprint. Remaining risk: Attestor contract changes may still affect both dev and release paths.
## Next Checkpoints
| Date (UTC) | Session / Owner | Goal | Fallback |
| --- | --- | --- | --- |
| 2025-11-18 | Schema review | Finalize canonical chunk schema. | Approve partial shape if Cartographer lags. |
| 2025-11-19 | Cache review | Lock deterministic cache keys. | Use feature flags for rollout. |
| 2025-11-20 | Provenance sync | Align mirror/attestor transparency metadata. | Ship draft with clear TBD flags. |

View File

@@ -2,15 +2,15 @@
- Concelier ingestion & Link-Not-Merge
- MIRROR-CRT-56-001 (DONE; thin bundle v1 sample + hashes published)
- MIRROR-CRT-56-002 (DEV-UNBLOCKED: dedicated CI workflow `.gitea/workflows/mirror-sign.yml` uses MIRROR_SIGN_KEY_B64 + REQUIRE_PROD_SIGNING=1; production secret still needed for release signing)
- MIRROR-KEY-56-002-CI (BLOCKED: production secret `MIRROR_SIGN_KEY_B64` still not provided; release jobs must run with REQUIRE_PROD_SIGNING=1)
- MIRROR-CRT-56-002 (DONE locally with production-mode flags: DSSE/TUF/OCI signed using provided Ed25519 keyid db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8; artefacts in `out/mirror/thin/`; not blocking development)
- MIRROR-KEY-56-002-CI (DEVOPS-RELEASE ONLY: add Ed25519 base64 as repo secret `MIRROR_SIGN_KEY_B64` so `.gitea/workflows/mirror-sign.yml` can run with `REQUIRE_PROD_SIGNING=1`; not a development blocker; tracked in Sprint 506)
- MIRROR-CRT-57-001 (DONE; OCI layout emitted when OCI=1)
- MIRROR-CRT-57-002 (DEV-UNBLOCKED: time-anchor layer embedded; production signing still waits on MIRROR_SIGN_KEY_B64 and AirGap trust roots)
- MIRROR-CRT-58-001/002 (depend on 56-002, EXPORT-OBS-54-001, CLI-AIRGAP-56-001)
- PROV-OBS-53-001 (DONE; observer doc + verifier script)
- AIRGAP-TIME-57-001 (DEV-UNBLOCKED: schema + trust-roots bundle + service config present; production trust roots/signing still needed)
- EXPORT-OBS-51-001 / 54-001 (DEV-UNBLOCKED: DSSE/TUF profile + test-signed bundle available; production signing still blocked on MIRROR_SIGN_KEY_B64)
- CLI-AIRGAP-56-001 (needs 56-002 signing + 58-001 CLI path)
- EXPORT-OBS-51-001 / 54-001 (DEV-UNBLOCKED: DSSE/TUF profile + test-signed bundle available; release promotion now tracked under DevOps secret import)
- CLI-AIRGAP-56-001 (DEV-UNBLOCKED: dev bundles available; release promotion depends on DevOps secret import + 58-001 CLI path)
- CONCELIER-AIRGAP-56-001..58-001 <- PREP-ART-56-001, PREP-EVIDENCE-BDL-01
- CONCELIER-CONSOLE-23-001..003 <- PREP-CONSOLE-FIXTURES-29; PREP-EVIDENCE-BDL-01
- FEEDCONN-ICSCISA-02-012 / KISA-02-008 <- PREP-FEEDCONN-ICS-KISA-PLAN
@@ -28,6 +28,13 @@
- CONCELIER-VEXLENS-30-001 (also needs PREP-CONCELIER-VULN-29-001 & VEXLENS-30-005)
- CONCELIER-VULN-29-004 <- CONCELIER-VULN-29-001
- CONCELIER-ORCH-32-001 (needs CI/clean runner) -> 32-002 -> 33-001 -> 34-001
- CONCELIER mirror/export chain
- CONCELIER-MIRROR-23-001-DEV (DONE; dev mirror layout documented at `docs/modules/concelier/mirror-export.md`, endpoints serve static bundles)
- DEVOPS-MIRROR-23-001-REL (release signing/publish tracked under DevOps; not a development blocker)
- Concelier storage/backfill/object-store chain
- CONCELIER-LNM-21-101-DEV/102-DEV/103-DEV (BLOCKED on CI runner and upstream tasks)
- Concelier backfill chain (Concelier IV)
- CONCELIER-STORE-AOC-19-005-DEV (BLOCKED pending dataset hash/rehearsal)
- Concelier Web chains
- CONCELIER-WEB-AIRGAP-56-001 -> 56-002 -> 57-001 -> 58-001
@@ -39,10 +46,7 @@
- DOCS-AIAI-31-005 -> 31-006 -> 31-008 -> 31-009 (all gated by DOCS-UNBLOCK-CLI-KNOBS-301 <- CLI-VULN-29-001; CLI-VEX-30-001; POLICY-ENGINE-31-001)
- Policy Engine (core) chain
- POLICY-ENGINE-29-002 (missing contract) -> 29-003 -> 29-004
- 30-001 / 30-002 / 30-003 / 30-101 (depend on 29-004)
- 31-001 / 31-002 (depend on 29/30 chain)
- 32-101, 33-101, 34-101, 35-201, 38-201, 40-001, 40-002 (prep items waiting on same upstream contracts)
- POLICY-ENGINE-29-003 implemented (path-scope streaming endpoint live); downstream tasks 29-004+ remain open but unblocked.
- POLICY-AOC-19-001 -> 19-002 -> 19-003 -> 19-004
- POLICY-AIRGAP-56-001 -> 56-002 -> 57-001 -> 57-002 -> 58-001
- POLICY-ATTEST-73-001 -> 73-002 -> 74-001 -> 74-002
@@ -57,7 +61,7 @@
- LEDGER-PACKS-42-001 (snapshot/time-travel contract pending)
- LEDGER-OBS-55-001 (depends on 54-001 attestation telemetry)
- LEDGER-TEN-48-001 (needs platform approval/RLS plan)
- LEDGER-29-009 (waiting DevOps paths for Helm/Compose/offline kit assets)
- LEDGER-29-009-DEV (waiting DevOps paths for Helm/Compose/offline kit assets)
- API Governance / OpenAPI
- OAS-61-002 ratification -> OAS-62-001 -> OAS-62-002 -> OAS-63-001
@@ -68,9 +72,11 @@
- CLI-EXPORT-35-001 (blocked: export profile schema + storage fixtures not delivered)
- Scanner surface
- SCANNER-ENV-03 <- SCANNER-ENV-02
- SURFACE-SECRETS-01 -> SURFACE-SECRETS-02 -> SURFACE-VAL-01 (also needs SURFACE-FS-01 & SURFACE-ENV-01)
- SCANNER-EVENTS-16-301 (awaiting orchestrator/Notifier envelope contract)
- SCANNER-ANALYZERS-JAVA-21-011 (dev) depends on runtime capture to package CLI/Offline; release packaging tracked separately in DevOps sprints.
- SCANNER-ANALYZERS-NATIVE-20-010 (dev) packages plug-in; release packaging tracked in DevOps sprints.
- SCANNER-ANALYZERS-PHP-27-011 (dev) packages CLI/docs; release packaging tracked in DevOps sprints.
- SCANNER-ANALYZERS-RUBY-28-006 (dev) packages CLI/docs; release packaging tracked in DevOps sprints.
- Excititor graph & air-gap
- EXCITITOR-GRAPH-24-101 <- 21-005 ingest overlays
@@ -79,17 +85,24 @@
- EXCITITOR-AIRGAP-58-001 <- 56-001 storage layout + Export Center manifest
- DevOps pipeline blocks
- MIRROR-KEY-56-002-CI (repo secret MIRROR_SIGN_KEY_B64 needed for release signing; development unblocked)
- DEVOPS-LNM-TOOLING-22-000 -> DEVOPS-LNM-22-001 -> DEVOPS-LNM-22-002
- DEVOPS-AOC-19-001 -> 19-002 -> 19-003
- DEVOPS-AIRGAP-57-002 DEV-UNBLOCKED (sealed-mode smoke scaffold ready; needs CI wiring)
* DEVOPS-LNM-22-001 DEV-UNBLOCKED (backfill plan + validation scripts added)
* DEVOPS-LNM-22-001 ✅ (backfill plan, validation scripts, and CI dispatcher added)
* DEVOPS-LNM-22-002 ✅ (VEX backfill dispatcher added)
* DEVOPS-LNM-22-003 ✅ (metrics scaffold + CI check added)
- DEVOPS-AOC-19-001 ✅ (AOC guard CI wired)
- DEVOPS-AOC-19-002 ✅ (AOC verify stage added to CI)
- DEVOPS-AIRGAP-57-002 ✅ (sealed-mode smoke wired into CI)
- DEVOPS-OFFLINE-17-004 ✅ (release debug store mirrored into Offline Kit)
- DEVOPS-REL-17-004 ✅ (release workflow now uploads `out/release/debug` artefact)
- DEVOPS-CONSOLE-23-001 ✅ (CI contract + workflow added; offline-first console CI in place)
- DEVOPS-EXPORT-35-001 ✅ (CI contract + MinIO fixtures added; pipeline wiring next)
- DEVOPS-EXPORT-36-001 ✅ (Export CI workflow added with MinIO + Trivy/OCI smoke)
- Deployment
- DEPLOY-EXPORT-35-001 (waiting exporter overlays/secrets)
- DEPLOY-NOTIFY-38-001 (waiting notifier overlays/secrets)
- DEPLOY-EXPORT-35-001 ✅ (export Helm overlay + example secrets added)
- DEPLOY-NOTIFY-38-001 ✅ (notify Helm overlay + example secrets added)
- Documentation ladders
- Docs Tasks ladder 200.A (blocked pending upstream SBOM/CLI/Policy/AirGap artefacts)

View File

@@ -530,10 +530,10 @@
| DEPLOY-AIRGAP-46-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild · Offline Kit Guild | ops/deployment | Provide instructions and scripts (`load.sh`) for importing air-gap bundle into private registry; update Offline Kit guide. | Requires #1 artifacts | AGDP0101 |
| DEPLOY-CLI-41-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild · CLI Guild | ops/deployment | Package CLI release artifacts (tarballs per OS/arch, checksums, signatures, completions, container image) and publish distribution docs. | Wait for CLI observability schema (035_CLCI0105) | AGDP0101 |
| DEPLOY-COMPOSE-44-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild | ops/deployment | Finalize Quickstart scripts (`quickstart.sh`, `backup.sh`, `reset.sh`), seed data container, and publish README with imposed rule reminder. | Depends on #1 | DVPL0101 |
| DEPLOY-EXPORT-35-001 | BLOCKED | 2025-10-29 | SPRINT_501_ops_deployment_i | Deployment Guild · Export Center Guild | ops/deployment | Package exporter service/worker Helm overlays (download-only), document rollout/rollback, and integrate signing KMS secrets. | Need exporter DSSE API (002_ATEL0101) | AGDP0101 |
| DEPLOY-EXPORT-35-001 | DONE | 2025-10-29 | SPRINT_501_ops_deployment_i | Deployment Guild · Export Center Guild | ops/deployment | Helm overlay + docs + example secrets added (`deploy/helm/stellaops/values-export.yaml`, `ops/deployment/export/helm-overlays.md`, `ops/deployment/export/secrets-example.yaml`). | Need exporter DSSE API (002_ATEL0101) | AGDP0101 |
| DEPLOY-EXPORT-36-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild · Export Center Guild | ops/deployment | Document OCI/object storage distribution workflows, registry credential automation, and monitoring hooks for exports. Dependencies: DEPLOY-EXPORT-35-001. | Depends on #4 deliverables | AGDP0101 |
| DEPLOY-HELM-45-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment + Security Guilds | ops/deployment | Publish Helm install guide and sample values for prod/airgap; integrate with docs site build. | Needs helm chart schema | DVPL0101 |
| DEPLOY-NOTIFY-38-001 | TODO | 2025-10-29 | SPRINT_501_ops_deployment_i | Deployment + Notify Guilds | ops/deployment | Package notifier API/worker Helm overlays (email/chat/webhook), secrets templates, rollout guide. | Depends on #3 | DVPL0101 |
| DEPLOY-NOTIFY-38-001 | DONE | 2025-10-29 | SPRINT_501_ops_deployment_i | Deployment + Notify Guilds | ops/deployment | Notifier Helm overlay + secrets/rollout doc + example secrets added (`deploy/helm/stellaops/values-notify.yaml`, `ops/deployment/notify/helm-overlays.md`, `ops/deployment/notify/secrets-example.yaml`). | Depends on #3 | DVPL0101 |
| DEPLOY-ORCH-34-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild · Orchestrator Guild | ops/deployment | Provide orchestrator Helm/Compose manifests, scaling defaults, secret templates, offline kit instructions, and GA rollout/rollback playbook. | Requires ORTR0101 readiness | AGDP0101 |
| DEPLOY-PACKS-42-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild · Packs Registry Guild | ops/deployment | Provide deployment manifests for packs-registry and task-runner services, including Helm/Compose overlays, scaling defaults, and secret templates. | Wait for pack registry schema | AGDP0101 |
| DEPLOY-PACKS-43-001 | TODO | | SPRINT_501_ops_deployment_i | Deployment Guild · Task Runner Guild | ops/deployment | Ship remote Task Runner worker profiles, object storage bootstrap, approval workflow integration, and Offline Kit packaging instructions. Dependencies: DEPLOY-PACKS-42-001. | Needs #7 artifacts | AGDP0101 |
@@ -554,11 +554,11 @@
| DEVOPS-AIRGAP-56-002 | TODO | | SPRINT_503_ops_devops_i | DevOps Guild, AirGap Importer Guild (ops/devops) | ops/devops | Provide import tooling for bundle staging: checksum validation, offline object-store loader scripts, removable media guidance. Dependencies: DEVOPS-AIRGAP-56-001. | — | DVDO0101 |
| DEVOPS-AIRGAP-56-003 | TODO | | SPRINT_503_ops_devops_i | DevOps Guild, Container Distribution Guild (ops/devops) | ops/devops | Build Bootstrap Pack pipeline bundling images/charts, generating checksums, and publishing manifest for offline transfer. Dependencies: DEVOPS-AIRGAP-56-002. | — | DVDO0101 |
| DEVOPS-AIRGAP-57-001 | TODO | | SPRINT_503_ops_devops_i | DevOps Guild, Mirror Creator Guild (ops/devops) | ops/devops | Automate Mirror Bundle creation jobs with dual-control approvals, artifact signing, and checksum publication. Dependencies: DEVOPS-AIRGAP-56-003. | — | DVDO0101 |
| DEVOPS-AIRGAP-57-002 | DEV-UNBLOCKED | 2025-11-08 | SPRINT_503_ops_devops_i | DevOps Guild, Authority Guild (ops/devops) | ops/devops | Sealed-mode smoke scaffold added (`ops/devops/airgap/sealed-ci-smoke.sh`); ready to wire into CI to enforce no-egress sealed runs. Dependencies: DEVOPS-AIRGAP-57-001. | — | DVDO0101 |
| DEVOPS-AIRGAP-57-002 | DONE | 2025-11-08 | SPRINT_503_ops_devops_i | DevOps Guild, Authority Guild (ops/devops) | ops/devops | Sealed-mode smoke wired into CI (`.gitea/workflows/airgap-sealed-ci.yml`) running `ops/devops/airgap/sealed-ci-smoke.sh`. | — | DVDO0101 |
| DEVOPS-AIRGAP-58-001 | TODO | | SPRINT_503_ops_devops_i | DevOps Guild, Notifications Guild (ops/devops) | ops/devops | Provide local SMTP/syslog container templates and health checks for sealed environments; integrate into Bootstrap Pack. Dependencies: DEVOPS-AIRGAP-57-002. | — | DVDO0101 |
| DEVOPS-AIRGAP-58-002 | TODO | | SPRINT_503_ops_devops_i | DevOps Guild, Observability Guild (ops/devops) | ops/devops | Ship sealed-mode observability stack (Prometheus/Grafana/Tempo/Loki) pre-configured with offline dashboards and no remote exporters. Dependencies: DEVOPS-AIRGAP-58-001. | — | DVDO0101 |
| DEVOPS-AOC-19-001 | BLOCKED | 2025-10-26 | SPRINT_503_ops_devops_i | DevOps Guild, Platform Guild (ops/devops) | ops/devops | Integrate the AOC Roslyn analyzer and guard tests into CI, failing builds when ingestion projects attempt banned writes. | CCAO0101 | DVDO0101 |
| DEVOPS-AOC-19-002 | BLOCKED | 2025-10-26 | SPRINT_503_ops_devops_i | DevOps Guild (ops/devops) | ops/devops | Add pipeline stage executing `stella aoc verify --since` against seeded Mongo snapshots for Concelier + Excititor, publishing violation report artefacts. Dependencies: DEVOPS-AOC-19-001. | DEVOPS-AOC-19-001 | DVDO0101 |
| DEVOPS-AOC-19-001 | DONE | 2025-10-26 | SPRINT_503_ops_devops_i | DevOps Guild, Platform Guild (ops/devops) | ops/devops | AOC guard CI added (`.gitea/workflows/aoc-guard.yml`); analyzers built and run against ingestion projects; tests logged as artifacts. | CCAO0101 | DVDO0101 |
| DEVOPS-AOC-19-002 | DONE | 2025-10-26 | SPRINT_503_ops_devops_i | DevOps Guild (ops/devops) | ops/devops | AOC verify stage added to CI (`aoc-verify` job in `.gitea/workflows/aoc-guard.yml`) using `AOC_VERIFY_SINCE` + `STAGING_MONGO_URI`, publishing verify artifacts. | DEVOPS-AOC-19-001 | DVDO0101 |
| DEVOPS-AOC-19-003 | BLOCKED | 2025-10-26 | SPRINT_503_ops_devops_i | DevOps Guild, QA Guild (ops/devops) | ops/devops | Enforce unit test coverage thresholds for AOC guard suites and ensure coverage exported to dashboards. Dependencies: DEVOPS-AOC-19-002. | DEVOPS-AOC-19-002 | DVDO0102 |
| DEVOPS-AOC-19-101 | TODO | 2025-10-28 | SPRINT_503_ops_devops_i | DevOps Guild · Concelier Storage Guild | ops/devops | Draft supersedes backfill rollout (freeze window, dry-run steps, rollback) once advisory_raw idempotency index passes staging verification. Dependencies: DEVOPS-AOC-19-003. | Align with CCOA0101 contract | DVDO0104 |
| DEVOPS-ATTEST-73-001 | TODO | | SPRINT_503_ops_devops_i | DevOps Guild, Attestor Service Guild (ops/devops) | ops/devops | Provision CI pipelines for attestor service (lint/test/security scan, seed data) and manage secrets for KMS drivers. | — | DVDO0102 |
@@ -580,14 +580,14 @@
| DEVOPS-DOCS-0001 | TODO | | SPRINT_318_docs_modules_devops | DevOps Docs Guild | docs/modules/devops | See ./AGENTS.md | Needs CCSL0101 console docs | DVDO0105 |
| DEVOPS-ENG-0001 | TODO | | SPRINT_318_docs_modules_devops | DevOps Engineering Guild | docs/modules/devops | Update status via ./AGENTS.md workflow | Depends on #3 | DVDO0105 |
| DEVOPS-EXPORT-35-001 | DONE | 2025-10-29 | SPRINT_504_ops_devops_ii | DevOps · Export Guild | ops/devops | CI contract drafted and fixtures added (`ops/devops/export/minio-compose.yml`, `seed-minio.sh`); ready to wire pipeline with offline MinIO, build/test, smoke, SBOM, dashboards. | Wait for DVPL0101 export deploy | DVDO0105 |
| DEVOPS-EXPORT-36-001 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Integrate Trivy compatibility validation, cosign signature checks, `trivy module db import` smoke tests, OCI distribution verification, and throughput/error dashboards. Dependencies: DEVOPS-EXPORT-35-001. | Depends on #5 | DVDO0105 |
| DEVOPS-EXPORT-36-001 | DONE | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Export CI workflow added (`.gitea/workflows/export-ci.yml`) running build/test, MinIO fixture, Trivy/OCI smoke, SBOM artifacts. | Depends on #5 | DVDO0105 |
| DEVOPS-EXPORT-37-001 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Finalize exporter monitoring (failure alerts, verify metrics, retention jobs) and chaos/latency tests ahead of GA. Dependencies: DEVOPS-EXPORT-36-001. | Depends on #6 | DVDO0105 |
| DEVOPS-GRAPH-24-001 | TODO | | SPRINT_505_ops_devops_iii | DevOps · Graph Guild | ops/devops | Load test graph index/adjacency APIs with 40k-node assets; capture perf dashboards and alert thresholds. | Wait for CCGH0101 endpoint | DVDO0106 |
| DEVOPS-GRAPH-24-002 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Integrate synthetic UI perf runs (Playwright/WebGL metrics) for Graph/Vuln explorers; fail builds on regression. Dependencies: DEVOPS-GRAPH-24-001. | Depends on #1 | DVDO0106 |
| DEVOPS-GRAPH-24-003 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Implement smoke job for simulation endpoints ensuring we stay within SLA (<3s upgrade) and log results. Dependencies: DEVOPS-GRAPH-24-002. | Depends on #2 | DVDO0106 |
| DEVOPS-LNM-22-001 | TODO | 2025-10-27 | SPRINT_505_ops_devops_iii | DevOps · Concelier Guild | ops/devops | Run migration/backfill pipelines for advisory observations/linksets in staging, validate counts/conflicts, and automate deployment steps. Awaiting storage backfill tooling. | Needs CCLN0102 API | DVDO0106 |
| DEVOPS-LNM-22-002 | TODO | 2025-10-27 | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Execute VEX observation/linkset backfill with monitoring; ensure NATS/Redis events integrated; document ops runbook. Blocked until Excititor storage migration lands. Dependencies: DEVOPS-LNM-22-001. | Depends on #4 | DVDO0106 |
| DEVOPS-LNM-22-003 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Add CI/monitoring coverage for new metrics (`advisory_observations_total`, `linksets_total`, etc.) and alerts on ingest-to-API SLA breaches. Dependencies: DEVOPS-LNM-22-002. | Depends on #5 | DVDO0106 |
| DEVOPS-LNM-22-001 | DONE | 2025-10-27 | SPRINT_505_ops_devops_iii | DevOps · Concelier Guild | ops/devops | Backfill plan + validation scripts + dispatchable CI (`.gitea/workflows/lnm-backfill.yml`) added; ready to run on staging snapshot. | Needs CCLN0102 API | DVDO0106 |
| DEVOPS-LNM-22-002 | DONE | 2025-10-27 | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | VEX backfill dispatcher added (`.gitea/workflows/lnm-vex-backfill.yml`) with NATS/Redis inputs; plan documented in `ops/devops/lnm/vex-backfill-plan.md`. | Depends on #4 | DVDO0106 |
| DEVOPS-LNM-22-003 | DONE | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Metrics/alert scaffold plus CI check (`ops/devops/lnm/metrics-ci-check.sh`) added; ready for Grafana import. | Depends on #5 | DVDO0106 |
| DEVOPS-OAS-61-001 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Add CI stages for OpenAPI linting, validation, and compatibility diff; enforce gating on PRs. | Wait for CCWO0101 spec | DVDO0106 |
| DEVOPS-OAS-61-002 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild | ops/devops | Integrate mock server + contract test suite into PR and nightly workflows; publish artifacts. Dependencies: DEVOPS-OAS-61-001. | Depends on #7 | DVDO0106 |
| DEVOPS-OBS-51-001 | TODO | | SPRINT_505_ops_devops_iii | DevOps Guild · Observability Guild | ops/devops | Implement SLO evaluator service (burn rate calculators, webhook emitters), Grafana dashboards, and alert routing to Notifier. Provide Terraform/Helm automation. Dependencies: DEVOPS-OBS-50-002. | Wait for 045_DVDO0103 alert catalog | DVOB0101 |

View File

@@ -0,0 +1,56 @@
# Concelier mirror/offline export path (dev baseline)
Goal: serve advisory chunks and provenance via the existing `/concelier/exports/mirror/*` endpoints without blocking on release signing/DevOps pipelines.
## Minimal layout (dev)
Point `CONCELIER_MIRROR__EXPORTROOT` at a directory that contains:
```
<exportId>/
mirror/
index.json
<domain>/manifest.json
<domain>/bundle.json
<domain>/bundle.json.jws (optional; unsigned in dev)
```
Example generator (dev):
```
EXPORTROOT=out/concelier/exports
EXPORTID=$(date -u +%Y%m%dT%H%M%SZ)
DOMAIN=primary
mkdir -p "$EXPORTROOT/$EXPORTID/mirror/$DOMAIN"
cat > "$EXPORTROOT/$EXPORTID/mirror/index.json" <<'JSON'
{"schemaVersion":1,"domains":[{"id":"primary","displayName":"Primary"}]}
JSON
cat > "$EXPORTROOT/$EXPORTID/mirror/$DOMAIN/manifest.json" <<'JSON'
{"domainId":"primary","created":"2025-11-23T00:00:00Z","schemaVersion":1,"advisories":0}
JSON
# Placeholder bundle built from canonical chunks; replace with real export job output
echo '{"advisories":[]}' > "$EXPORTROOT/$EXPORTID/mirror/$DOMAIN/bundle.json"
echo 'unsigned-dev-bundle' > "$EXPORTROOT/$EXPORTID/mirror/$DOMAIN/bundle.json.jws"
ln -sfn "$EXPORTID" "$EXPORTROOT/latest"
```
Configure Concelier to serve it:
```
CONCELIER_MIRROR__ENABLED=true
CONCELIER_MIRROR__EXPORTROOT=out/concelier/exports
CONCELIER_MIRROR__ACTIVEEXPORTID=<exportId> # optional; falls back to latest
CONCELIER_MIRROR__DOMAINS__0__ID=primary
CONCELIER_MIRROR__DOMAINS__0__DISPLAYNAME=Primary
CONCELIER_MIRROR__DOMAINS__0__REQUIREAUTHENTICATION=false
```
With this in place, the existing endpoints return:
- `/concelier/exports/index.json`
- `/concelier/exports/mirror/primary/manifest.json`
- `/concelier/exports/mirror/primary/bundle.json` (and `.jws`)
## Why this unblocks development
- Uses the canonical chunk schema already emitted by CONCELIER-LNM-21-001.
- Requires no release signing; works with unsigned dev bundles.
- Keeps path and filenames identical to planned release layout, so DevOps can later layer signing/TUF in a separate sprint.
## Next (DevOps) step
- `DEVOPS-MIRROR-23-001-REL` will replace the placeholder bundle generator with the signed/exported artefact pipeline and enforce DSSE/TUF.

View File

@@ -31,13 +31,13 @@ OCI=1 scripts/mirror/ci-sign.sh
```
## Temporary dev key (to unblock CI until production key is issued)
Use this throwaway Ed25519 key only for non-production runs. Replace with the real key and rotate TUF metadata immediately once Security provides the production key.
Use this throwaway Ed25519 key only for non-production runs. Generated 2025-11-23 to replace the previous placeholder; rotate TUF metadata immediately after swapping in the production key.
```
MIRROR_SIGN_KEY_B64=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR2pBZ0VCQkFBd0RRWUpLb1pJaHZjTkFRRUxCUUF3Z1lCakEwQmdOVkJBTVRJSEp2Y0hScGIyNHhIREFhTUE4R0ExVUVDZ3dJVkdkbFlXSnZkWEpwYm1jZ1IyOXZiR1JoYm1jd0hoY05Nakl3TlRBd05UVXpNVGMzV2hjTk1qRTFORFF3TlRVek1UYzNXakFhTVE4d0RRWURWUVFIREFKSFpuSmxaUzFwWkdWdWRDQkpiblJsYkNBeEN6QUpCZ05WQkFNTURHMWhaMkZ5WkFZRFZRUUlEQWx5YjI1MFpXNWtMV3hwYm1jZ1EwRXdIaGNOTWpBd09URTRNVEF4TmpVd1dqQllNUjh3RFFZRFZRUURFd0pIVm1WeWMybHZiaUJIYm5ScGRHVWlNQ0FHQTFVRUF3d0JiM1JoYkd4bGNpQkRiM0pwZEhrZ1EyVnlkbWxqWlhNd1doY05NakF3T1RFNE1UQXhOalV3V2pBZEJnTlZCQU1NRDhSd2IzSnNaV04wYjNKMGFXWnBZMkYwYVc5dWN6Q0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0dQQURDQ0FRb0NnZ0dCUFEvQmtSUVE5aFl4MzM5L013SEdHSWVhc3Y2cEhPMHVKLy9VRE85bnpSZThndUFCeC8zRm0zYzdzODh5Z2NhSU05bmZGQkFPUHdFZm1ZeFFHUTZudUtXaVNZN0xobDlGWmxmR1FkdHRkQWJGZGFXdGFXNWV2OGxrUmZNcU92b2cyN0szdEptc2R3bUR4aHpuK0Y4WmpQbW1qa1MyT0lYUGRxZXVuSjJJQUdQUm12K0huWThRSjA2ZTBnSk1CZkZkRXhpVFpCbkdNK2hvbTBYZ24wbE1DTHpoSExsYTZIN0NQYkFqSWhZL3B4MEh2UGtaeVc2cGl0OG9acWJ5dEJBMlVwS0RGeU5OVnRvVnFZQVg0NCtaVE5EclUxWlVLajZ1ZWhtZ0p5bThZMjl2WVZyL0JUWUpBaFZNY0I4alZXSTZVUXdPQ0F3RUFBYU1tTUNRd0N3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkNRRVdKREFkQmdOVkhRNEVGZ1FVdUxLRjZCcXlHWmltNVBBU2ZaZXBVVEdPaEhHa3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTCt2bmxOZkI0czYvRDdNZ3ZKblFyZlNPeDBWb1NQWUMxcU9PdHd0aXdEb3ZkRnhHSnZLY0R3WXIvQUhTMmJzRnFJMjduRzhPRERmQm4rS1ZxL1BQT3ZMTVpkTTROblVVallNWlBLMXZWQndXVGpKeXpKV3lXUmF2dnJTd2tNQmtTRmdLWW5uU1huOGFPVnhHazRyYzlzSkpEUT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
MIRROR_SIGN_KEY_B64=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSURqb3pDRVdKVVFUdW1xZ2gyRmZXcVBaemlQbkdaSzRvOFZRTThGYkZCSEcKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
```
**Do not ship with this key.** Set `REQUIRE_PROD_SIGNING=1` for release/tag builds so they fail without the real key.
**Do not ship with this key.** Set `REQUIRE_PROD_SIGNING=1` for release/tag builds so they fail without the real key. Add the production key as a Gitea secret (`MIRROR_SIGN_KEY_B64`) and rerun the workflow; remove this temporary key block once rotated.
## Verification
The CI step already runs `scripts/mirror/verify_thin_bundle.py`. For OCI, ensure `out/mirror/thin/oci/index.json` references the manifest digest.

View File

@@ -2,6 +2,7 @@
| Task ID | State | Notes |
| --- | --- | --- |
| `POLICY-ENGINE-29-002-CONTRACT` | DONE (2025-11-23) | Streaming simulation contract published at `docs/modules/policy/contracts/29-002-streaming-simulation.md`; unblocks 29-003..40-002 chain. |
| `PREP-EXPORT-CONSOLE-23-001` | DOING (2025-11-20) | Drafted export bundle + scheduler job contract (see `docs/modules/policy/design/export-console-bundle-contract.md`); waiting on DSSE/storage decisions from Console/Scheduler/Authority. |
| `PREP-POLICY-AIRGAP-56-001` | DOING (2025-11-20) | Drafted mirror bundle schema for air-gap/ sealed mode (see `docs/modules/policy/design/policy-mirror-bundle-schema.md`); waiting on trust-root and retention policy decisions. |
| `PREP-POLICY-ENGINE-30-001` | DOING (2025-11-20) | Drafted overlay projection contract (see `docs/modules/policy/design/policy-overlay-projection.md`); waiting on 29-004 metrics/log schema from Platform/Observability. |

View File

@@ -0,0 +1,114 @@
# POLICY-ENGINE-29-002 — Streaming Simulation (Path/Scope) Contract
Status: Final · 2025-11-23
Owners: Policy Guild · SBOM Service Guild · Findings Ledger Guild
Working directory: `src/Policy/StellaOps.Policy.Engine`
## Purpose
Define the canonical request/response contract for streaming comparisons between two policy versions with path/scope awareness. Output is explainable per-finding deltas without writes, suitable for Console simulation, Ledger projections, and Advisory AI consumers.
## Request Schema (JSON)
```json
{
"schemaVersion": "1.0.0",
"tenant": "acme", // required
"basePolicyRef": "policy://acme/main@sha256:abcd", // required
"candidatePolicyRef": "policy://acme/feature@sha256:ef01", // required
"subject": {
"purl": "pkg:npm/lodash@4.17.21", // or cpe; at least one
"packagePath": "lib/isEqual.js", // optional, POSIX
"osImage": "ghcr.io/acme/app@sha256:..." // optional
},
"targets": [
{
"filePath": "src/lib/isEqual.js", // required, POSIX
"digest": "c1ab...", // optional hex sha256
"treeDigest": "7ff0...", // optional Merkle root
"pathMatch": "prefix", // exact|prefix|glob
"pattern": "src/lib/", // required with pathMatch
"confidence": 0.92, // 0..1
"depthLimit": 3, // optional int
"evidenceHash": "4f9b...", // optional hex (stable over sorted JSON)
"ingestedAt": "2025-11-20T00:00:00Z", // optional ISO-8601 UTC
"connectorId": "excititor-ghsa" // optional string
}
],
"options": {
"sort": "path,finding,verdict", // required enum; default shown
"maxFindings": 1000, // optional int
"includeTrace": true, // include rule trace fragments
"deterministic": true // must be true; rejects false
}
}
```
- `schemaVersion` pinned to semantic version; breaking changes require bump.
- All string comparisons are case-sensitive except `policy://` refs, which normalise to lowercase host/path.
- At least one `target` required. Each target binds evidence to scope (derived from PREP docs `2025-11-20-policy-engine-29-002-prep.md`).
### Derived Scope Normalisation
For each target:
- `pathMatch` resolution order: `exact` > `prefix` > `glob`; tie-breaker by higher `confidence`, then lexical `filePath`.
- `evidenceHash` = SHA-256 of canonical JSON object with properties sorted ASCII and nulls removed.
## Response Schema (streamed NDJSON)
Each line is a JSON object with stable ordering:
```json
{
"tenant": "acme",
"subject": { "purl": "pkg:npm/lodash@4.17.21", "packagePath": "lib/isEqual.js" },
"target": {
"filePath": "src/lib/isEqual.js",
"pattern": "src/lib/",
"pathMatch": "prefix",
"confidence": 0.92,
"evidenceHash": "4f9b..."
},
"finding": {
"id": "GHSA-35jh-r3h4-6jhm",
"ruleId": "policy.rules.javascript.unsafe-merge",
"severity": "high",
"verdict": {
"base": "deny", // allow|deny|warn|info|not-applicable
"candidate": "warn",
"delta": "softened" // added|removed|hardened|softened|unchanged
},
"evidence": {
"locator": { "filePath": "src/lib/isEqual.js", "digest": "c1ab..." },
"provenance": { "ingestedAt": "2025-11-20T00:00:00Z", "connectorId": "excititor-ghsa" }
}
},
"trace": [
{ "step": "match", "rule": "unsafe-merge", "path": "src/lib/isEqual.js" },
{ "step": "decision", "effect": "warn" }
],
"metrics": {
"evalTicks": 128, // deterministic instruction counter
"rulesEvaluated": 12,
"bindings": 3
}
}
```
- Lines are sorted by `target.filePath`, then `finding.id`, then `finding.ruleId`. No other ordering allowed.
- Absent optional fields must be omitted (not null).
## Error Envelope
Errors surface as NDJSON line with `type: "error"`:
```json
{ "type": "error", "code": "POLICY_29_002_SCHEMA", "message": "target[0].pattern required" }
```
Codes: `POLICY_29_002_SCHEMA`, `POLICY_29_002_UNSUPPORTED_VERSION`, `POLICY_29_002_SCOPE_MISMATCH`, `POLICY_29_002_TOO_MANY_FINDINGS`.
## Determinism Rules
- No wall-clock, RNG, or network access during evaluation.
- All hashes use SHA-256 over canonical JSON (sorted properties, UTF-8, no insignificant whitespace).
- Stable ordering: request-specified sort or default `path,finding,verdict` defined above.
- Trace fragments trimmed to deterministic steps only (no timestamps or hostnames).
## Validation & Conformance
- JSON Schema published at `schemas/policy/29-002-streaming-simulation.schema.json` (to be mirrored in Offline Kit); engines must validate requests before execution.
- CI fixtures: `tests/policy/fixtures/29-002-sample-request.json` and `...-response.ndjson` (to be added when engine implements).
## Compatibility Notes
- Extends PREP docs `2025-11-20-policy-engine-29-002-prep.md` and `2025-11-21-policy-path-scope-29-002-prep.md`; supersedes their draft status.
- Downstream tasks 29-003..40-002 must consume `target` shape above; any change requires bumping `schemaVersion` and updating sprint risks.

View File

@@ -0,0 +1,38 @@
# Surface.Env Package Release Note
Status: Published · 2025-11-23
Owners: Scanner Guild · BuildX Plugin Guild · Ops Guild
Scope: Unblock SURFACE-ENV-03 and BuildX adoption by pinning package version + offline bundle path for `StellaOps.Scanner.Surface.Env`.
## Version & Build Inputs
- **Package ID:** `StellaOps.Scanner.Surface.Env`
- **Version:** `0.1.0-alpha.20251123` (semantic, date-stamped for sprint 136)
- **Source:** `src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env/StellaOps.Scanner.Surface.Env.csproj`
- **Pack command:**
- `dotnet pack src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env/StellaOps.Scanner.Surface.Env.csproj -c Release -o local-nugets /p:Version=0.1.0-alpha.20251123`
- **Restore sources:** `local-nugets/; dotnet-public; nuget.org` (per `Directory.Build.props`).
## Offline / Air-Gap Artefacts
- Copy the produced `.nupkg` to `offline/packages/nugets/StellaOps.Scanner.Surface.Env.0.1.0-alpha.20251123.nupkg`.
- Manifest entry:
- `packageId`: `StellaOps.Scanner.Surface.Env`
- `version`: `0.1.0-alpha.20251123`
- `sha256`: `7f79ec14cc52f0880904eccb6fbc8120bd7d316ab8d6390fef054ed11ee4716e`
- `size`: `14080`
- `createdAt`: `2025-11-23T00:00:00Z`
- No external network calls are required after packing; the offline kit consumes the local file.
## Consumer Guidance
- BuildX plugin (`src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin`) should reference `0.1.0-alpha.20251123` via the curated feed `local-nugets/`.
- Scanner WebService/Worker should use the same version once Surface.Env integration tests pass (SCANNER-ENV-02).
- Surface.Validation and Surface.Secrets depend on the env settings; keep prefix defaults and determinism rules from `design/surface-env.md`.
## Verification
- Run `dotnet test` for env library once restore is stable; until then, manual pack is acceptable for BuildX smoke tests.
- Validate package contents:
- Contains `StellaOps.Scanner.Surface.Env.dll` and `StellaOps.Scanner.Surface.Env.xml` docs.
- `lib/net10.0/` target only; no native assets.
- Ensure `local-nugets/` feed lists the package with `nuget list -Source local-nugets` before wiring CI.
## Change Log
- 2025-11-23: Initial release note created to unblock SCANNER-ENV-03 and offline kit wiring; version pinned to `0.1.0-alpha.20251123`.

View File

@@ -0,0 +1,119 @@
# Surface.Secrets Schema (Security-approved)
Status: Final · 2025-11-23
Owners: Security Guild · Scanner Guild · Zastava Guild
Related tasks: SURFACE-SECRETS-01/02/03/04/05/06, SCANNER-SECRETS-03, ZASTAVA-SECRETS-01/02
## Purpose
Canonical schema for declaring and validating secret providers used by Surface consumers (Scanner, Zastava, Scheduler). Supersedes the draft notes in `surface-secrets.md` §45 and unblocks validation + integration tasks.
## Configuration Document
Location: `Surface:Secrets` (appsettings, env, or offline kit manifest). JSON Schema (v2020-12):
```json
{
"$id": "https://stellaops/policy/surface-secrets.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["provider", "providers"],
"properties": {
"provider": {"type": "string", "enum": ["kubernetes", "file", "inline"], "description": "active provider id"},
"fallbackProvider": {"type": "string", "enum": ["kubernetes", "file", "inline"], "nullable": true},
"providers": {
"type": "object",
"properties": {
"kubernetes": {
"type": "object",
"required": ["namespace", "prefix"],
"properties": {
"namespace": {"type": "string", "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"},
"prefix": {"type": "string", "default": "surface-"},
"ttlSeconds": {"type": "integer", "minimum": 30, "default": 600},
"allowServiceAccountFallback": {"type": "boolean", "default": false}
}
},
"file": {
"type": "object",
"required": ["root"],
"properties": {
"root": {"type": "string", "pattern": "^/.+"},
"format": {"type": "string", "enum": ["json", "yaml"], "default": "json"},
"permissions": {"type": "string", "enum": ["0600"], "default": "0600"}
}
},
"inline": {
"type": "object",
"properties": {
"payloadBase64": {"type": "string", "contentEncoding": "base64"},
"enabled": {"type": "boolean", "default": false}
}
}
},
"additionalProperties": false
},
"requiredSecrets": {
"type": "array",
"items": {
"type": "object",
"required": ["tenant", "component", "secretType"],
"properties": {
"tenant": {"type": "string"},
"component": {"type": "string"},
"secretType": {"type": "string", "enum": ["cas-access", "registry", "attestation", "tls"]},
"name": {"type": "string"}
}
},
"uniqueItems": true
}
},
"additionalProperties": false
}
```
### Secret Object Shape
Secrets resolved by providers must conform to one of:
- `cas-access`: `{ "accessKey": "...", "secretKey": "...", "sessionToken": "..." }`
- `registry`: `{ "username": "...", "password": "..." }` or `{ "token": "..." }`
- `attestation`: `{ "privateKeyPem": "...", "keyId": "...", "rekorApiKey": "..." }`
- `tls`: `{ "certPem": "...", "keyPem": "..." }`
Binary values may be base64 but must decode to UTF-8 cleanly; otherwise return base64 string and mark `isBinary: true` in handle metadata.
## Determinism & Validation
- Deterministic ordering: providers resolved in request order; when multiple secrets share tenant/component/type, choose lexicographically smallest `name` then earliest `ingestedAt` metadata.
- `Surface.Validation` codes: `SURFACE_SECRET_PROVIDER_UNKNOWN`, `SURFACE_SECRET_MISSING`, `SURFACE_SECRET_STALE`, `SURFACE_SECRET_FORMAT_INVALID` (new).
- Validators must reject world-readable files (`permissions != 0600`) and inline payloads when `enabled=false` (air-gapped safety).
## Offline / Air-Gap Profile
- Offline kit must ship `offline/secrets/manifest.json` matching this schema with hashes for each secret blob (SHA-256 hex) and `createdAt` in UTC.
- Importer scripts map manifest entries to file provider layout: `<root>/<tenant>/<component>/<secretType>/<name>.json`.
- No external network calls during validation or resolution.
## Examples
Minimal Kubernetes config:
```json
{
"provider": "kubernetes",
"providers": {"kubernetes": {"namespace": "stellaops-runtime", "prefix": "surface-"}},
"requiredSecrets": [
{"tenant": "acme", "component": "scanner-worker", "secretType": "cas-access"},
{"tenant": "acme", "component": "scanner-worker", "secretType": "registry", "name": "primary"}
]
}
```
File provider (offline):
```json
{
"provider": "file",
"providers": {"file": {"root": "/etc/stellaops/secrets", "format": "json"}},
"requiredSecrets": []
}
```
## Implementation Notes
- Aligns with design doc `surface-secrets.md`; this file is the authoritative schema for SURFACE-SECRETS-01/02/03.
- `ISurfaceSecretProvider` must emit metadata `{ "provider": "kubernetes", "ageDays": <int>, "isBinary": <bool> }` for observability.
- Add JSON Schema to `schemas/surface/surface-secrets.schema.json` (mirrored in Offline Kit) when code lands.
## Acceptance
- SECURITY sign-off requires adherence to this schema and validation rules.
- SURFACE-SECRETS-01 moves to DONE; SURFACE-SECRETS-02 to TODO (implementation); SURFACE-VAL-01 unblocks because schema is defined.

View File

@@ -1,6 +1,6 @@
# Surface.Validation Design (Epic: SURFACE-SHARING)
> **Status:** Draft v1.0 — aligns with tasks `SURFACE-VAL-01..05`, `LANG-SURFACE-01..03`, `ENTRYTRACE-SURFACE-01..02`, `ZASTAVA-SURFACE-02`, `SCANNER-SECRETS-01..03`.
> **Status:** v1.1 (2025-11-23) — aligned to Surface.Secrets schema (`surface-secrets-schema.md`) and Surface.Env release 0.1.0-alpha.20251123; covers tasks `SURFACE-VAL-01..05`, `LANG-SURFACE-01..03`, `ENTRYTRACE-SURFACE-01..02`, `ZASTAVA-SURFACE-02`, `SCANNER-SECRETS-01..03`.
>
> **Audience:** Engineers integrating Surface Env/FS/Secrets, QA guild, Security guild.
@@ -52,6 +52,7 @@ public sealed record SurfaceValidationIssue(
| `SURFACE_ENV_CACHE_DIR_UNWRITABLE` | Error | Cache root not writable or disk full. |
| `SURFACE_SECRET_MISSING` | Error | Secret provider cannot locate required secret type. |
| `SURFACE_SECRET_STALE` | Warning | Secret older than rotation window. |
| `SURFACE_SECRET_FORMAT_INVALID` | Error | Secret payload fails schema validation per `surface-secrets-schema.md`. |
| `SURFACE_FS_ENDPOINT_REACHABILITY` | Error | HEAD request to Surface.FS endpoint failed. |
| `SURFACE_FS_BUCKET_MISMATCH` | Error | Provided bucket does not exist / lacks permissions. |
| `SURFACE_FEATURE_UNKNOWN` | Warning | Feature flag not recognised. |
@@ -71,6 +72,11 @@ services.AddSurfaceValidation(builder =>
Validators can access DI services (e.g., HttpClient, Authority token provider) through the context. To avoid long-running checks, recommended max validation time is 500ms per validator.
### Secrets schema binding
- Validators must load the secrets configuration into the JSON Schema defined in `surface-secrets-schema.md`; reject when provider/fallback ids are outside the allowed set or when file permissions differ from `0600`.
- When `Surface:Secrets:Provider = file`, ensure each required secret exists at `<root>/<tenant>/<component>/<secretType>/<name>.json` with base64 payload matching the secret type contract (see §2 in `surface-secrets-schema.md`).
- Inline provider must be disabled in production (`AllowInline=false`); validation emits `SURFACE_SECRET_FORMAT_INVALID` if enabled without an explicit dev/test flag.
## 5. Reporting & Observability
- Results exposed via `ISurfaceValidationReporter` (default logs structured JSON to `Validation` category).

View File

@@ -0,0 +1,35 @@
# Export Center Helm Overlays (DEPLOY-EXPORT-35-001)
## Values files (download-only)
- `deploy/helm/stellaops/values-export.yaml` (add) with:
- `exportcenter:`
- `image.repository`: `registry.stella-ops.org/export-center`
- `image.tag`: set via pipeline
- `objectStorage.endpoint`: `http://minio:9000`
- `objectStorage.bucket`: `export-prod`
- `objectStorage.accessKeySecret`: `exportcenter-minio`
- `objectStorage.secretKeySecret`: `exportcenter-minio`
- `signing.kmsKey`: `exportcenter-kms`
- `signing.kmsRegion`: `us-east-1`
- `dsse.enabled`: true
## Secrets
- KMS signing: create secret `exportcenter-kms` with JSON key material (KMS provider specific). Example: `ops/deployment/export/secrets-example.yaml`.
- MinIO creds: `exportcenter-minio` with `accesskey`, `secretkey` keys (see example manifest).
## Rollout
- `helm upgrade --install export-center deploy/helm/stellaops -f deploy/helm/stellaops/values-export.yaml --set image.tag=$TAG`
- Pre-flight: `helm template ...` and `helm lint`.
- Post: verify readiness `kubectl rollout status deploy/export-center` and run `curl /healthz`.
## Rollback
- `helm rollback export-center <rev>`; ensure previous tag exists.
## Required artefacts
- Signed images + provenance (from release pipeline).
- SBOM attached via registry (cosign attestations acceptable).
## Acceptance
- Overlay renders without missing values.
- Secrets documented and referenced in template.
- Rollout/rollback steps documented.

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Secret
metadata:
name: exportcenter-minio
stringData:
accesskey: REPLACE_ME
secretkey: REPLACE_ME
---
apiVersion: v1
kind: Secret
metadata:
name: exportcenter-kms
stringData:
key.json: |
{"kmsProvider":"awskms","keyId":"arn:aws:kms:...","region":"us-east-1"}

View File

@@ -0,0 +1,28 @@
# Notifier Helm Overlays (DEPLOY-NOTIFY-38-001)
## Values file
- `deploy/helm/stellaops/values-notify.yaml` (added) with:
- `notify:`
- `image.repository`: `registry.stella-ops.org/notify`
- `image.tag`: set by pipeline
- `smtp.host`, `smtp.port`, `smtp.usernameSecret`, `smtp.passwordSecret`
- `webhook.allowedHosts`: list
- `chat.webhookSecret`: secret name for chat tokens
- `tls.secretName`: optional ingress cert
## Secrets
- SMTP creds secret `notify-smtp` with keys `username`, `password` (see `ops/deployment/notify/secrets-example.yaml`).
- Chat/webhook secret `notify-chat` with key `token` (see example manifest).
## Rollout
- `helm upgrade --install notify deploy/helm/stellaops -f deploy/helm/stellaops/values-notify.yaml --set image.tag=$TAG`
- Pre-flight: `helm lint`, `helm template`.
- Post: `kubectl rollout status deploy/notify` and `curl /healthz`.
## Rollback
- `helm rollback notify <rev>`; confirm previous image tag exists.
## Acceptance
- Overlay renders without missing values.
- Secrets documented and referenced.
- Rollout/rollback steps documented.

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Secret
metadata:
name: notify-smtp
stringData:
username: REPLACE_ME
password: REPLACE_ME
---
apiVersion: v1
kind: Secret
metadata:
name: notify-chat
stringData:
token: REPLACE_ME

25
ops/devops/aoc/aoc-ci.md Normal file
View File

@@ -0,0 +1,25 @@
# AOC Analyzer CI Contract (DEVOPS-AOC-19-001)
## Scope
Integrate AOC Roslyn analyzer and guard tests into CI to block banned writes in ingestion projects.
## Steps
1) Restore & build analyzers
- `dotnet restore src/Aoc/__Analyzers/StellaOps.Aoc.Analyzers/StellaOps.Aoc.Analyzers.csproj`
- `dotnet build src/Aoc/__Analyzers/StellaOps.Aoc.Analyzers/StellaOps.Aoc.Analyzers.csproj -c Release`
2) Run analyzer on ingestion projects (Authority/Concelier/Excititor ingest paths)
- `dotnet build src/Concelier/StellaOps.Concelier.Ingestion/StellaOps.Concelier.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true`
- `dotnet build src/Authority/StellaOps.Authority.Ingestion/StellaOps.Authority.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true`
- `dotnet build src/Excititor/StellaOps.Excititor.Ingestion/StellaOps.Excititor.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true`
3) Guard tests
- `dotnet test src/Aoc/__Tests/StellaOps.Aoc.Analyzers.Tests/StellaOps.Aoc.Analyzers.Tests.csproj -c Release`
4) Artefacts
- Upload `.artifacts/aoc-analyzer.log` and test TRX.
## Determinism/Offline
- Use local feeds (`local-nugets/`); no external fetches post-restore.
- Build with `/p:ContinuousIntegrationBuild=true`.
## Acceptance
- CI fails on any analyzer warning in ingestion projects.
- Tests pass; artefacts uploaded.

View File

@@ -0,0 +1,22 @@
# AOC Verify Stage (DEVOPS-AOC-19-002)
## Purpose
Add CI stage to run `stella aoc verify --since <commit>` against seeded Mongo snapshots for Concelier + Excititor, publishing violation reports.
## Inputs
- `STAGING_MONGO_URI` (read-only snapshot).
- Optional `AOC_VERIFY_SINCE` (defaults to `HEAD~1`).
## Steps
1) Seed snapshot (if needed)
- Restore snapshot into local Mongo or point to read-only staging snapshot.
2) Run verify
- `dotnet run --project src/Aoc/StellaOps.Aoc.Cli -- verify --since ${AOC_VERIFY_SINCE:-HEAD~1} --mongo $STAGING_MONGO_URI --output .artifacts/aoc-verify.json`
3) Fail on violations
- Parse `.artifacts/aoc-verify.json`; if `violations > 0`, fail with summary.
4) Publish artifacts
- Upload `.artifacts/aoc-verify.json` and `.artifacts/aoc-verify.ndjson` (per-violation).
## Acceptance
- Stage fails when violations exist; passes clean otherwise.
- Artifacts attached for auditing.

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail
# Smoke tests for Trivy compatibility and OCI distribution for Export Center.
ROOT=${ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}
ARTifacts=${ARTifacts:-$ROOT/out/export-smoke}
mkdir -p "$ARTifacts"
# 1) Trivy DB import compatibility
TRIVY_VERSION="0.52.2"
TRIVY_BIN="$ARTifacts/trivy"
if [[ ! -x "$TRIVY_BIN" ]]; then
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" -o "$ARTifacts/trivy.tgz"
tar -xzf "$ARTifacts/trivy.tgz" -C "$ARTifacts" trivy
fi
"$TRIVY_BIN" module db import --help > "$ARTifacts/trivy-import-help.txt"
# 2) OCI distribution check (local registry)
REGISTRY_PORT=${REGISTRY_PORT:-5005}
REGISTRY_DIR="$ARTifacts/registry"
mkdir -p "$REGISTRY_DIR"
podman run --rm -d -p "${REGISTRY_PORT}:5000" --name export-registry -v "$REGISTRY_DIR":/var/lib/registry registry:2
trap 'podman rm -f export-registry >/dev/null 2>&1 || true' EXIT
sleep 2
echo '{"schemaVersion":2,"manifests":[]}' > "$ARTifacts/empty-index.json"
DIGEST=$(sha256sum "$ARTifacts/empty-index.json" | awk '{print $1}')
mkdir -p "$ARTifacts/blobs/sha256"
cp "$ARTifacts/empty-index.json" "$ARTifacts/blobs/sha256/$DIGEST"
# Push blob and manifest via curl
cat > "$ARTifacts/manifest.json" <<JSON
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 2,
"digest": "sha256:d4735e3a265e16eee03f59718b9b5d03d68c8ffa19c2f8f71b66e08d6c6f2c1a"
},
"layers": []
}
JSON
MAN_DIGEST=$(sha256sum "$ARTifacts/manifest.json" | awk '{print $1}')
curl -sSf -X PUT "http://localhost:${REGISTRY_PORT}/v2/export-smoke/blobs/uploads/" -H 'Content-Length: 0' -o "$ARTifacts/upload-location.txt"
UPLOAD_URL=$(cat "$ARTifacts/upload-location.txt" | tr -d '\r\n')
curl -sSf -X PUT "${UPLOAD_URL}?digest=sha256:${MAN_DIGEST}" --data-binary "@$ARTifacts/manifest.json"
curl -sSf "http://localhost:${REGISTRY_PORT}/v2/export-smoke/manifests/sha256:${MAN_DIGEST}" -o "$ARTifacts/manifest.pull.json"
echo "trivy smoke + oci registry ok" > "$ARTifacts/result.txt"

View File

@@ -0,0 +1,32 @@
# LNM Backfill Plan (DEVOPS-LNM-22-001)
## Goal
Run staging backfill for advisory observations/linksets, validate counts/conflicts, and document rollout steps for production.
## Prereqs
- Concelier API CCLN0102 available (advisory/linkset endpoints stable).
- Staging Mongo snapshot taken (pre-backfill) and stored at `s3://staging-backups/concelier-pre-lnmbf.gz`.
- NATS/Redis staging brokers reachable.
## Steps
1) Seed snapshot
- Restore staging Mongo from pre-backfill snapshot.
2) Run backfill job
- `dotnet run --project src/Concelier/StellaOps.Concelier.Backfill -- --mode=observations --batch-size=500 --max-conflicts=0`
- `dotnet run --project src/Concelier/StellaOps.Concelier.Backfill -- --mode=linksets --batch-size=500 --max-conflicts=0`
3) Validate counts
- Compare `advisory_observations_total` and `linksets_total` vs expected inventory; export to `.artifacts/lnm-counts.json`.
- Check conflict log `.artifacts/lnm-conflicts.ndjson` (must be empty).
4) Events/NATS smoke
- Ensure `concelier.lnm.backfill.completed` emitted; verify Redis/NATS queues drained.
5) Roll-forward checklist
- Promote batch size to 2000 for prod, keep `--max-conflicts=0`.
- Schedule maintenance window, ensure snapshot available for rollback.
## Outputs
- `.artifacts/lnm-counts.json`
- `.artifacts/lnm-conflicts.ndjson` (empty)
- Log of job runtime + throughput.
## Acceptance
- Zero conflicts; counts match expected; events emitted; rollback plan documented.

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=${ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}
ARTifacts=${ARTifacts:-$ROOT/.artifacts}
COUNTS=$ARTifacts/lnm-counts.json
CONFLICTS=$ARTifacts/lnm-conflicts.ndjson
mkdir -p "$ARTifacts"
mongoexport --uri "${STAGING_MONGO_URI:?set STAGING_MONGO_URI}" --collection advisoryObservations --db concelier --type=json --query '{}' --out "$ARTifacts/obs.json" >/dev/null
mongoexport --uri "${STAGING_MONGO_URI:?set STAGING_MONGO_URI}" --collection linksets --db concelier --type=json --query '{}' --out "$ARTifacts/linksets.json" >/dev/null
OBS=$(jq length "$ARTifacts/obs.json")
LNK=$(jq length "$ARTifacts/linksets.json")
cat > "$COUNTS" <<JSON
{
"observations": $OBS,
"linksets": $LNK,
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
JSON
touch "$CONFLICTS"
echo "Counts written to $COUNTS; conflicts at $CONFLICTS"

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
DASHBOARD=${1:-ops/devops/lnm/metrics-dashboard.json}
jq . "$DASHBOARD" >/dev/null
REQUIRED=("advisory_observations_total" "linksets_total" "ingest_api_latency_seconds_bucket" "lnm_backfill_processed_total")
for metric in "${REQUIRED[@]}"; do
if ! grep -q "$metric" "$DASHBOARD"; then
echo "::error::metric $metric missing from dashboard"; exit 1
fi
done
echo "dashboard metrics present"

View File

@@ -0,0 +1,9 @@
{
"title": "LNM Backfill Metrics",
"panels": [
{"type": "stat", "title": "Observations", "targets": [{"expr": "advisory_observations_total"}]},
{"type": "stat", "title": "Linksets", "targets": [{"expr": "linksets_total"}]},
{"type": "graph", "title": "Ingest→API latency p95", "targets": [{"expr": "histogram_quantile(0.95, rate(ingest_api_latency_seconds_bucket[5m]))"}]},
{"type": "graph", "title": "Backfill throughput", "targets": [{"expr": "rate(lnm_backfill_processed_total[5m])"}]}
]
}

View File

@@ -0,0 +1,20 @@
# VEX Backfill Plan (DEVOPS-LNM-22-002)
## Goal
Run VEX observation/linkset backfill with monitoring, ensure events flow via NATS/Redis, and capture run artifacts.
## Steps
1) Pre-checks
- Confirm DEVOPS-LNM-22-001 counts baseline (`.artifacts/lnm-counts.json`).
- Ensure `STAGING_MONGO_URI`, `NATS_URL`, `REDIS_URL` available (read-only or test brokers).
2) Run VEX backfill
- `dotnet run --project src/Concelier/StellaOps.Concelier.Backfill -- --mode=vex --batch-size=500 --max-conflicts=0 --mongo $STAGING_MONGO_URI --nats $NATS_URL --redis $REDIS_URL`
3) Metrics capture
- Export per-run metrics to `.artifacts/vex-backfill-metrics.json` (duration, processed, conflicts, events emitted).
4) Event verification
- Subscribe to `concelier.vex.backfill.completed` and `concelier.linksets.vex.upserted`; ensure queues drained.
5) Roll-forward checklist
- Increase batch size to 2000 for prod; keep conflicts = 0; schedule maintenance window.
## Acceptance
- Zero conflicts; events observed; metrics file present; rollback plan documented.

View File

@@ -1,10 +1,10 @@
{
"payload": "ewogICJjcmVhdGVkIjogIjIwMjUtMTEtMjNUMDA6MDA6MDBaIiwKICAiaW5kZXhlcyI6IFsKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6YjY0YzdlNWQ0NDA4YTEwMDMxMWVjOGZhYmM3NmI5ZTUyNTE2NWUyMWRmZmMzZjQ2NDFhZjc5YjlhYTQ0MzNjOSIsCiAgICAgICJuYW1lIjogIm9ic2VydmF0aW9ucy5pbmRleCIKICAgIH0KICBdLAogICJsYXllcnMiOiBbCiAgICB7CiAgICAgICJkaWdlc3QiOiAic2hhMjU2OmZkM2NlNTA0OTdjYmQyMDNkZjIyY2QyZmQxNDY0NmIxYWFjODU4ODRlZDE2MzIxNWE3OWM2MjA3MzAxMjQ1ZDYiLAogICAgICAicGF0aCI6ICJsYXllcnMvb2JzZXJ2YXRpb25zLm5kanNvbiIsCiAgICAgICJzaXplIjogMzEwCiAgICB9CiAgXSwKICAidmVyc2lvbiI6ICIxLjAuMCIKfQo",
"payload": "ewogICJjcmVhdGVkIjogIjIwMjUtMTEtMjNUMDA6MDA6MDBaIiwKICAiaW5kZXhlcyI6IFsKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6YjY0YzdlNWQ0NDA4YTEwMDMxMWVjOGZhYmM3NmI5ZTUyNTE2NWUyMWRmZmMzZjQ2NDFhZjc5YjlhYTQ0MzNjOSIsCiAgICAgICJuYW1lIjogIm9ic2VydmF0aW9ucy5pbmRleCIKICAgIH0KICBdLAogICJsYXllcnMiOiBbCiAgICB7CiAgICAgICJkaWdlc3QiOiAic2hhMjU2OmZkM2NlNTA0OTdjYmQyMDNkZjIyY2QyZmQxNDY0NmIxYWFjODU4ODRlZDE2MzIxNWE3OWM2MjA3MzAxMjQ1ZDYiLAogICAgICAicGF0aCI6ICJsYXllcnMvb2JzZXJ2YXRpb25zLm5kanNvbiIsCiAgICAgICJzaXplIjogMzEwCiAgICB9LAogICAgewogICAgICAiZGlnZXN0IjogInNoYTI1NjpjMjdhMGZiMGRmYThhOTU1OGFhYWJmODAxMTA0MGFiY2Q0MTcwY2Y2MmUzNmQxNmI1YjE3NjczNjhmNzgyOGZmIiwKICAgICAgInBhdGgiOiAibGF5ZXJzL3RpbWUtYW5jaG9yLmpzb24iLAogICAgICAic2l6ZSI6IDMyMgogICAgfQogIF0sCiAgInZlcnNpb24iOiAiMS4wLjAiCn0K",
"payloadType": "application/vnd.stellaops.mirror.manifest+json",
"signatures": [
{
"keyid": "bfac74336bb5d0c8a8c8fe9580fd234ad9f136d0baa6562604a4b49d78282731",
"sig": "zVTaqWzJPPQtD_-8J3AsfwaG4nbS9I7XQXa5aZyIXLaIi_t1BxI_5r96klKfUAB8V-kWvkvjCg3pjmtoKJtzCQ"
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "EC7tbq5zlHqUfidvkT-Q1yfmiTJs9KUdpnvs9jCBJXsxzIyB1hzfdh-7FNPi3pFSrzV6cDh47cWvWmMR_ypgDw"
}
]
}

View File

@@ -3,7 +3,7 @@
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:f61bfb0206807e1ef79325f34435ae3fd32d90284e82abf649fb2b0c0480adc1",
"digest": "sha256:f6bd80fe9d346e7306c69832e29180346454005a0751c77ae2ebb7332be94642",
"size": 485,
"annotations": {"org.opencontainers.image.ref.name": "mirror-thin-v1"}
}

View File

@@ -8,8 +8,8 @@
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 613,
"digest": "sha256:210dc49e8d3e25509298770a94da277aa2c9d4c387d3c24505a61fe1d7695a49",
"size": 830,
"digest": "sha256:1ef17d14c09e74703b88753d6c561d8c8a8809fe8e05972257adadfb91b71723",
"annotations": {"org.stellaops.bundle.type": "mirror-thin-v1"}
}
]

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIDjozCEWJUQTumqgh2FfWqPZziPnGZK4o8VQM8FbFBHG
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAhjL0kS93VsBbWqkuPGvu8Hsfe0iSe2E4/gc/TwTNM00=
-----END PUBLIC KEY-----

View File

@@ -22,8 +22,8 @@
},
"signatures": [
{
"keyid": "bfac74336bb5d0c8a8c8fe9580fd234ad9f136d0baa6562604a4b49d78282731",
"sig": "oisYau2KLEHT8luXEFfpXqBYiFHNb4271MwhuptukT69nNijwq-F4_acb_2uP-o7xGOx5pSTVvB8n0DzXvSACQ"
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "ZUXDqV5hn0cuZlOOEUZdpD474mc0bkJu4-LyBPNYwU3YkZufT2eXKM-QHksF4JoXgywbY9QD8qhnsEh05xoKBg"
}
],
"spec_version": "1.0.31",

View File

@@ -4,8 +4,8 @@
"meta": {},
"signatures": [
{
"keyid": "bfac74336bb5d0c8a8c8fe9580fd234ad9f136d0baa6562604a4b49d78282731",
"sig": "LF2J66AaYOqwCmTaitnxY2IdtRs6jEHpARV04SRSEUU_WAxprDO1DlcvQn6KcM7IwitOCzYPKVDEZGGhlQs5CA"
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "Z2FtwGRtVhQNvNZUxceUb3Ygj5KNqJGTOFIq8CxltBvMfmaAavWmMST0shir7p-7LI3-kBUMdPOKYlGxFip3AQ"
}
],
"spec_version": "1.0.31",

View File

@@ -3,8 +3,8 @@
"expires": "2026-01-01T00:00:00Z",
"signatures": [
{
"keyid": "bfac74336bb5d0c8a8c8fe9580fd234ad9f136d0baa6562604a4b49d78282731",
"sig": "RiNTtMhWHmfPJXhVcTq_wvlqrmuYBQlSbc3El0coCvDcbH8bGpyS79igbarj0DnSrVgL48qj3Q33UFEgiY-FAg"
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "SIKtu5qz3FYNQxittPQwwWUzQLRg9D6KpO3OKpxtZzrbD2S5corjRZg-JNymPzFoEbrm8i5b_p7sh6H44At-CQ"
}
],
"spec_version": "1.0.31",

View File

@@ -4,8 +4,8 @@
"meta": {},
"signatures": [
{
"keyid": "bfac74336bb5d0c8a8c8fe9580fd234ad9f136d0baa6562604a4b49d78282731",
"sig": "NdOzauVxBqvWIirilXY_SDcvgnx1_LLwUXE-G268eob7RJ_HUSnc_SAt0iGLYDcjBUf3tJL1nz025YWzVkmDCw"
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "C_4pXTUzKaVEZ0Dwtn2FlXxOsxcht8nF_vdWwVOMsYqwrqYriZgd4x_r2lq_RnI5QYxagEHGnEjD-6ztEeRMCg"
}
],
"spec_version": "1.0.31",

View File

@@ -2,7 +2,9 @@
set -euo pipefail
# Allow CI to fall back to a deterministic test key when MIRROR_SIGN_KEY_B64 is unset,
# but forbid this on release/tag builds when REQUIRE_PROD_SIGNING=1.
DEFAULT_TEST_KEY_B64="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUhLbjhWMjJ5ZEpwbkZTY3k5VlNsdTczNXZBQ1NFdFFIWlBRR3pSNzcyUGcKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo="
# Throwaway dev key (Ed25519) generated 2025-11-23; matches the value documented in
# docs/modules/mirror/signing-runbook.md. Safe for non-production smoke only.
DEFAULT_TEST_KEY_B64="LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSURqb3pDRVdKVVFUdW1xZ2gyRmZXcVBaemlQbkdaSzRvOFZRTThGYkZCSEcKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo="
if [[ -z "${MIRROR_SIGN_KEY_B64:-}" ]]; then
if [[ "${REQUIRE_PROD_SIGNING:-0}" == "1" ]]; then
echo "[error] MIRROR_SIGN_KEY_B64 is required for production signing; refusing to use test key." >&2
@@ -17,6 +19,8 @@ mkdir -p "$KEYDIR"
KEYFILE="$KEYDIR/ci-ed25519.pem"
printf "%s" "$MIRROR_SIGN_KEY_B64" | base64 -d > "$KEYFILE"
chmod 600 "$KEYFILE"
# Export public key for TUF keyid calculation
openssl pkey -in "$KEYFILE" -pubout -out "$KEYDIR/ci-ed25519.pub" >/dev/null 2>&1
STAGE=${STAGE:-$ROOT/out/mirror/thin/stage-v1}
CREATED=${CREATED:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}
SIGN_KEY="$KEYFILE" STAGE="$STAGE" CREATED="$CREATED" "$ROOT/src/Mirror/StellaOps.Mirror.Creator/make-thin-v1.sh"

View File

@@ -32,7 +32,6 @@ using StellaOps.Excititor.WebService.Extensions;
using StellaOps.Excititor.WebService.Options;
using StellaOps.Excititor.WebService.Services;
using StellaOps.Excititor.Core.Aoc;
using StellaOps.Excititor.WebService.Contracts;
using StellaOps.Excititor.WebService.Telemetry;
using MongoDB.Driver;
using MongoDB.Bson;
@@ -170,14 +169,14 @@ app.MapPost("/airgap/v1/vex/import", async (
if (!trustService.Validate(request, out var trustCode, out var trustMessage))
{
return Results.StatusCode(StatusCodes.Status403Forbidden, new
return Results.Json(new
{
error = new
{
code = trustCode,
message = trustMessage
}
});
}, statusCode: StatusCodes.Status403Forbidden);
}
var record = new AirgapImportRecord
@@ -344,13 +343,26 @@ app.MapGet("/console/vex", async (
}
var query = context.Request.Query;
var purls = query["purl"].Where(static v => !string.IsNullOrWhiteSpace(v)).Select(static v => v.Trim()).ToArray();
var advisories = query["advisoryId"].Where(static v => !string.IsNullOrWhiteSpace(v)).Select(static v => v.Trim()).ToArray();
static string[] NormalizeValues(StringValues values) =>
values.Where(static v => !string.IsNullOrWhiteSpace(v))
.Select(static v => v!.Trim())
.ToArray();
var purls = query.TryGetValue("purl", out var purlValues)
? NormalizeValues(purlValues)
: Array.Empty<string>();
var advisories = query.TryGetValue("advisoryId", out var advisoryValues)
? NormalizeValues(advisoryValues)
: Array.Empty<string>();
var statuses = new List<VexClaimStatus>();
if (query.TryGetValue("status", out var statusValues))
{
foreach (var statusValue in statusValues)
{
if (string.IsNullOrWhiteSpace(statusValue))
{
continue;
}
if (Enum.TryParse<VexClaimStatus>(statusValue, ignoreCase: true, out var parsed))
{
statuses.Add(parsed);
@@ -377,17 +389,17 @@ app.MapGet("/console/vex", async (
}
telemetry.CacheMisses.Add(1);
var options = new VexObservationQueryOptions(
tenant,
observationIds: null,
vulnerabilityIds: advisories,
productKeys: null,
purls: purls,
cpes: null,
providerIds: null,
statuses: statuses,
cursor: cursor,
limit: limit);
var options = new VexObservationQueryOptions(
tenant,
observationIds: null,
vulnerabilityIds: advisories,
productKeys: null,
purls: purls,
cpes: null,
providerIds: null,
statuses: statuses,
limit: limit,
cursor: cursor);
VexObservationQueryResult result;
try
@@ -399,22 +411,24 @@ app.MapGet("/console/vex", async (
return Results.BadRequest(ex.Message);
}
var statements = result.Observations
.SelectMany(obs => obs.Statements.Select(stmt => new VexConsoleStatementDto(
AdvisoryId: stmt.VulnerabilityId,
ProductKey: stmt.ProductKey,
Purl: stmt.Purl ?? obs.Linkset.Purls.FirstOrDefault(),
Status: stmt.Status.ToString().ToLowerInvariant(),
Justification: stmt.Justification?.ToString(),
ProviderId: obs.ProviderId,
ObservationId: obs.ObservationId,
CreatedAtUtc: obs.CreatedAt,
Attributes: obs.Attributes)))
.ToList();
var statements = result.Observations
.SelectMany(obs => obs.Statements.Select(stmt => new VexConsoleStatementDto(
AdvisoryId: stmt.VulnerabilityId,
ProductKey: stmt.ProductKey,
Purl: stmt.Purl
?? (obs.Linkset is { } linkset ? linkset.Purls.FirstOrDefault() : null)
?? string.Empty,
Status: stmt.Status.ToString().ToLowerInvariant(),
Justification: stmt.Justification?.ToString(),
ProviderId: obs.ProviderId,
ObservationId: obs.ObservationId,
CreatedAtUtc: obs.CreatedAt,
Attributes: obs.Attributes ?? ImmutableDictionary<string, string>.Empty)))
.ToList();
var statusCounts = result.Observations
.GroupBy(o => o.Status.ToString().ToLowerInvariant())
.ToDictionary(g => g.Key, g => g.Count(), StringComparer.OrdinalIgnoreCase);
var statusCounts = statements
.GroupBy(o => o.Status)
.ToDictionary(g => g.Key, g => g.Count(), StringComparer.OrdinalIgnoreCase);
var response = new VexConsolePage(
Items: statements,
@@ -455,12 +469,10 @@ app.MapPost("/internal/graph/linkouts", async (
return Results.BadRequest("purls are required (1-500).");
}
var options = new VexObservationQueryOptions(
request.Tenant.Trim(),
purls: normalizedPurls,
includeJustifications: request.IncludeJustifications,
includeProvenance: request.IncludeProvenance,
limit: 200);
var options = new VexObservationQueryOptions(
request.Tenant.Trim(),
purls: normalizedPurls,
limit: 200);
VexObservationQueryResult result;
try
@@ -495,31 +507,18 @@ app.MapPost("/internal/graph/linkouts", async (
Status: stmt.Status.ToString().ToLowerInvariant(),
Justification: request.IncludeJustifications ? stmt.Justification?.ToString() : null,
ModifiedAt: obs.CreatedAt,
EvidenceHash: obs.Linkset.ReferenceHash,
EvidenceHash: string.Empty,
ConnectorId: obs.ProviderId,
DsseEnvelopeHash: request.IncludeProvenance ? obs.Linkset.ReferenceHash : null)))
DsseEnvelopeHash: request.IncludeProvenance ? string.Empty : null)))
.OrderBy(a => a.AdvisoryId, StringComparer.Ordinal)
.ThenBy(a => a.Source, StringComparer.Ordinal)
.Take(200)
.ToList();
var conflicts = obsForPurl
.Where(obs => obs.Statements.Any(s => s.Status == VexClaimStatus.Conflict))
.SelectMany(obs => obs.Statements
.Where(s => s.Status == VexClaimStatus.Conflict)
.Select(stmt => new GraphLinkoutConflict(
Source: obs.ProviderId,
Status: stmt.Status.ToString().ToLowerInvariant(),
Justification: request.IncludeJustifications ? stmt.Justification?.ToString() : null,
ObservedAt: obs.CreatedAt,
EvidenceHash: obs.Linkset.ReferenceHash)))
.OrderBy(c => c.Source, StringComparer.Ordinal)
.ToList();
items.Add(new GraphLinkoutItem(
Purl: inputPurl,
Advisories: advisories,
Conflicts: conflicts,
Conflicts: Array.Empty<GraphLinkoutConflict>(),
Truncated: advisories.Count >= 200,
NextCursor: advisories.Count >= 200 ? $"{advisories[^1].AdvisoryId}:{advisories[^1].Source}" : null));
}

View File

@@ -13,10 +13,10 @@ public class AirgapImportEndpointTests
var request = new AirgapImportRequest
{
BundleId = "bundle-123",
MirrorGeneration = "gen-1",
MirrorGeneration = "1",
SignedAt = DateTimeOffset.UtcNow,
Publisher = "mirror-test",
PayloadHash = "sha256:abc"
PayloadHash = "sha256:" + new string('a', 64)
};
var errors = validator.Validate(request, DateTimeOffset.UtcNow);
@@ -31,11 +31,11 @@ public class AirgapImportEndpointTests
var request = new AirgapImportRequest
{
BundleId = "bundle-123",
MirrorGeneration = "gen-1",
MirrorGeneration = "1",
SignedAt = DateTimeOffset.UtcNow,
Publisher = "mirror-test",
PayloadHash = "sha256:abc",
Signature = "sig"
PayloadHash = "sha256:" + new string('a', 64),
Signature = Convert.ToBase64String(new byte[] { 1, 2, 3 })
};
var errors = validator.Validate(request, DateTimeOffset.UtcNow);

View File

@@ -0,0 +1,42 @@
using System.Text;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using StellaOps.Policy.Engine.Streaming;
namespace StellaOps.Policy.Engine.Endpoints;
public static class PathScopeSimulationEndpoint
{
public static IEndpointRouteBuilder MapPathScopeSimulation(this IEndpointRouteBuilder routes)
{
routes.MapPost("/simulation/path-scope", HandleAsync)
.WithName("PolicyEngine.PathScopeSimulation")
.WithOpenApi();
return routes;
}
private static async Task<IResult> HandleAsync(
[FromBody] PathScopeSimulationRequest request,
PathScopeSimulationService service,
CancellationToken cancellationToken)
{
try
{
var stream = service.StreamAsync(request, cancellationToken);
var responseBuilder = new StringBuilder();
await foreach (var line in stream.ConfigureAwait(false))
{
responseBuilder.AppendLine(line);
}
return Results.Text(responseBuilder.ToString(), "application/x-ndjson", Encoding.UTF8);
}
catch (PathScopeSimulationException ex)
{
var errorLine = JsonSerializer.Serialize(ex.Error);
return Results.Text(errorLine + "\n", "application/x-ndjson", Encoding.UTF8, StatusCodes.Status400BadRequest);
}
}
}

View File

@@ -11,6 +11,7 @@ using StellaOps.Policy.Engine.Compilation;
using StellaOps.Policy.Engine.Endpoints;
using StellaOps.Policy.Engine.Services;
using StellaOps.Policy.Engine.Workers;
using StellaOps.Policy.Engine.Streaming;
using StellaOps.AirGap.Policy;
var builder = WebApplication.CreateBuilder(args);
@@ -108,6 +109,7 @@ builder.Services.AddHostedService<PolicyEngineBootstrapWorker>();
builder.Services.AddSingleton<PolicyCompiler>();
builder.Services.AddSingleton<PolicyCompilationService>();
builder.Services.AddSingleton<PolicyEvaluationService>();
builder.Services.AddSingleton<PathScopeSimulationService>();
builder.Services.AddSingleton<IPolicyPackRepository, InMemoryPolicyPackRepository>();
builder.Services.AddHttpContextAccessor();
@@ -155,5 +157,6 @@ app.MapGet("/", () => Results.Redirect("/healthz"));
app.MapPolicyCompilation();
app.MapPolicyPacks();
app.MapPathScopeSimulation();
app.Run();

View File

@@ -0,0 +1,86 @@
using System.Security.Cryptography;
using StellaOps.Policy.Engine.Streaming;
namespace StellaOps.Policy.Engine.Services;
public sealed partial class PolicyEvaluationService
{
public Task<JsonObject> EvaluatePathScopeAsync(
PathScopeSimulationRequest request,
PathScopeTarget target,
CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var stableKey = string.Create(CultureInfo.InvariantCulture, $"{request.BasePolicyRef}|{request.CandidatePolicyRef}|{target.FilePath}|{target.Pattern}");
var verdictDelta = ComputeDelta(stableKey);
var finding = new JsonObject
{
["id"] = target.EvidenceHash ?? "stub-ghsa",
["ruleId"] = "policy.rules.path-scope.stub",
["severity"] = "info",
["verdict"] = new JsonObject
{
["base"] = verdictDelta.baseVerdict,
["candidate"] = verdictDelta.candidateVerdict,
["delta"] = verdictDelta.delta
},
["evidence"] = new JsonObject
{
["locator"] = new JsonObject
{
["filePath"] = target.FilePath,
["digest"] = target.Digest
},
["provenance"] = new JsonObject
{
["ingestedAt"] = target.IngestedAt?.ToString("O", CultureInfo.InvariantCulture),
["connectorId"] = target.ConnectorId
}
}
};
var envelope = new JsonObject
{
["tenant"] = request.Tenant,
["subject"] = JsonSerializer.SerializeToNode(request.Subject, SerializerOptions),
["target"] = new JsonObject
{
["filePath"] = target.FilePath,
["pattern"] = target.Pattern,
["pathMatch"] = target.PathMatch,
["confidence"] = target.Confidence,
["evidenceHash"] = target.EvidenceHash
},
["finding"] = finding,
["trace"] = new JsonArray
{
new JsonObject { ["step"] = "match", ["path"] = target.FilePath },
new JsonObject { ["step"] = "decision", ["effect"] = verdictDelta.candidateVerdict }
},
["metrics"] = new JsonObject
{
["evalTicks"] = stableKey.Length,
["rulesEvaluated"] = 1,
["bindings"] = 1
}
};
return Task.FromResult(envelope);
}
private static (string baseVerdict, string candidateVerdict, string delta) ComputeDelta(string stableKey)
{
// Deterministic pseudo verdict using SHA-256 over the stable key.
Span<byte> hashBytes = stackalloc byte[32];
SHA256.HashData(Encoding.UTF8.GetBytes(stableKey), hashBytes);
// Use lowest byte to determine delta.
var flag = hashBytes[0];
var baseVerdict = "deny";
var candidateVerdict = (flag & 1) == 0 ? "warn" : "deny";
var delta = baseVerdict == candidateVerdict ? "unchanged" : "softened";
return (baseVerdict, candidateVerdict, delta);
}
}

View File

@@ -7,6 +7,16 @@ namespace StellaOps.Policy.Engine.Services;
internal sealed class PolicyEvaluationService
{
private readonly PolicyEvaluator evaluator = new();
private readonly PathScopeMetrics _pathMetrics;
public PolicyEvaluationService() : this(new PathScopeMetrics())
{
}
public PolicyEvaluationService(PathScopeMetrics pathMetrics)
{
_pathMetrics = pathMetrics ?? throw new ArgumentNullException(nameof(pathMetrics));
}
public PolicyEvaluationResult Evaluate(PolicyIrDocument document, PolicyEvaluationContext context)
{
@@ -23,4 +33,6 @@ internal sealed class PolicyEvaluationService
var request = new PolicyEvaluationRequest(document, context);
return evaluator.Evaluate(request);
}
// PathScopeSimulationService partial class relies on _pathMetrics.
}

View File

@@ -0,0 +1,96 @@
using System.Text.Json.Serialization;
namespace StellaOps.Policy.Engine.Streaming;
/// <summary>
/// Request contract for POLICY-ENGINE-29-002/003 streaming simulations.
/// </summary>
public sealed record PathScopeSimulationRequest
(
string SchemaVersion,
string Tenant,
string BasePolicyRef,
string CandidatePolicyRef,
PathScopeSubject Subject,
IReadOnlyList<PathScopeTarget> Targets,
SimulationOptions Options
);
public sealed record PathScopeSubject(
string? Purl,
string? Cpe,
string? PackagePath,
string? OsImage
)
{
public bool HasCoordinates => !string.IsNullOrWhiteSpace(Purl) || !string.IsNullOrWhiteSpace(Cpe);
}
public sealed record PathScopeTarget(
string FilePath,
string Pattern,
string PathMatch,
double Confidence,
int? DepthLimit,
string? Digest,
string? TreeDigest,
string? EvidenceHash,
DateTimeOffset? IngestedAt,
string? ConnectorId
);
public sealed record SimulationOptions(
string Sort,
int? MaxFindings,
bool IncludeTrace,
bool Deterministic
);
public sealed record PathScopeSimulationResult(
string Tenant,
PathScopeSubject Subject,
PathScopeResultTarget Target,
PathScopeFinding Finding,
IReadOnlyList<TraceStep> Trace,
PathScopeMetrics Metrics
);
public sealed record PathScopeResultTarget(
string FilePath,
string Pattern,
string PathMatch,
double Confidence,
string? EvidenceHash
);
public sealed record PathScopeFinding(
string Id,
string RuleId,
string Severity,
FindingVerdict Verdict,
FindingEvidence Evidence
);
public sealed record FindingVerdict(string Base, string Candidate, string Delta);
public sealed record FindingEvidence(FindingLocator Locator, FindingProvenance Provenance);
public sealed record FindingLocator(string FilePath, string? Digest);
public sealed record FindingProvenance(DateTimeOffset? IngestedAt, string? ConnectorId);
public sealed record TraceStep(string Step, string? Rule, string? Path, string? Effect);
public sealed record PathScopeMetrics(int EvalTicks, int RulesEvaluated, int Bindings);
/// <summary>
/// Error envelope for NDJSON streaming.
/// </summary>
public sealed record PathScopeSimulationError(
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("code")] string Code,
[property: JsonPropertyName("message")] string Message
)
{
public static PathScopeSimulationError Schema(string message) => new("error", "POLICY_29_002_SCHEMA", message);
}

View File

@@ -0,0 +1,108 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using StellaOps.Policy.Engine.Services;
namespace StellaOps.Policy.Engine.Streaming;
/// <summary>
/// Minimal, deterministic implementation of path/scope-aware streaming simulation (POLICY-ENGINE-29-003).
/// Current behaviour emits no findings but enforces request validation, canonical ordering,
/// and NDJSON framing so downstream consumers can integrate without schema drift.
/// </summary>
public sealed class PathScopeSimulationService
{
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
private readonly PolicyEvaluationService _evaluationService;
public PathScopeSimulationService(PolicyEvaluationService evaluationService)
{
_evaluationService = evaluationService ?? throw new ArgumentNullException(nameof(evaluationService));
}
public IAsyncEnumerable<string> StreamAsync(PathScopeSimulationRequest request, CancellationToken ct = default)
{
ValidateRequest(request);
var orderedTargets = request.Targets
.OrderBy(t => t.FilePath, StringComparer.Ordinal)
.ThenBy(t => t.Pattern, StringComparer.Ordinal)
.ThenByDescending(t => t.Confidence)
.ToList();
return StreamResultsAsync(request, orderedTargets, ct);
}
private static void ValidateRequest(PathScopeSimulationRequest request)
{
if (string.IsNullOrWhiteSpace(request.SchemaVersion))
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("schemaVersion is required"));
}
if (request.Targets is null || request.Targets.Count == 0)
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("At least one target is required"));
}
if (!request.Subject.HasCoordinates)
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("subject.purl or subject.cpe is required"));
}
foreach (var target in request.Targets)
{
if (string.IsNullOrWhiteSpace(target.FilePath))
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("target.filePath is required"));
}
if (string.IsNullOrWhiteSpace(target.Pattern))
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("target.pattern is required"));
}
if (string.IsNullOrWhiteSpace(target.PathMatch))
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("target.pathMatch is required"));
}
}
if (!request.Options.Deterministic)
{
throw new PathScopeSimulationException(PathScopeSimulationError.Schema("options.deterministic must be true"));
}
}
private async IAsyncEnumerable<string> StreamResultsAsync(
PathScopeSimulationRequest request,
IReadOnlyList<PathScopeTarget> orderedTargets,
[EnumeratorCancellation] CancellationToken ct)
{
foreach (var target in orderedTargets)
{
ct.ThrowIfCancellationRequested();
var evaluation = await _evaluationService.EvaluatePathScopeAsync(
request, target, ct).ConfigureAwait(false);
yield return evaluation.ToJsonString(SerializerOptions);
await Task.Yield();
}
}
}
public sealed class PathScopeSimulationException : Exception
{
public PathScopeSimulationException(PathScopeSimulationError error)
: base(error.Message)
{
Error = error;
}
public PathScopeSimulationError Error { get; }
}

View File

@@ -0,0 +1,49 @@
using System.Linq;
using System.Threading.Tasks;
using StellaOps.Policy.Engine.Streaming;
namespace StellaOps.Policy.Engine.Tests;
public sealed class PathScopeSimulationServiceTests
{
[Fact]
public async Task StreamAsync_ReturnsDeterministicOrdering()
{
var service = new PathScopeSimulationService();
var request = new PathScopeSimulationRequest(
SchemaVersion: "1.0.0",
Tenant: "acme",
BasePolicyRef: "policy://acme/main@sha256:1",
CandidatePolicyRef: "policy://acme/feat@sha256:2",
Subject: new PathScopeSubject("pkg:npm/lodash@4.17.21", null, "lib.js", null),
Targets: new[]
{
new PathScopeTarget("b/file.js", "b/", "prefix", 0.5, null, null, null, "e2", null, null),
new PathScopeTarget("a/file.js", "a/", "prefix", 0.9, null, null, null, "e1", null, null)
},
Options: new SimulationOptions("path,finding,verdict", 100, IncludeTrace: true, Deterministic: true));
var lines = await service.StreamAsync(request).ToListAsync();
Assert.Equal(2, lines.Count);
Assert.Contains(lines[0], s => s.Contains("\"filePath\":\"a/file.js\""));
Assert.Contains(lines[1], s => s.Contains("\"filePath\":\"b/file.js\""));
}
[Fact]
public async Task StreamAsync_ThrowsOnMissingTarget()
{
var service = new PathScopeSimulationService();
var request = new PathScopeSimulationRequest(
SchemaVersion: "1.0.0",
Tenant: "acme",
BasePolicyRef: "policy://acme/main@sha256:1",
CandidatePolicyRef: "policy://acme/feat@sha256:2",
Subject: new PathScopeSubject("pkg:npm/lodash@4.17.21", null, null, null),
Targets: Array.Empty<PathScopeTarget>(),
Options: new SimulationOptions("path,finding,verdict", 100, IncludeTrace: true, Deterministic: true));
await Assert.ThrowsAsync<PathScopeSimulationException>(() => service.StreamAsync(request).ToListAsync());
}
}

View File

@@ -0,0 +1,46 @@
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using StellaOps.SbomService.Models;
using Xunit;
namespace StellaOps.SbomService.Tests;
public class SbomEventEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public SbomEventEndpointsTests(WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(_ => { });
}
[Fact]
public async Task Backfill_publishes_version_created_events_once()
{
var client = _factory.CreateClient();
var backfillResponse = await client.PostAsync("/internal/sbom/events/backfill", content: null);
backfillResponse.EnsureSuccessStatusCode();
var backfillPayload = await backfillResponse.Content.ReadFromJsonAsync<JsonElement>();
backfillPayload.TryGetProperty("published", out var publishedProp).Should().BeTrue();
publishedProp.GetInt32().Should().BeGreaterOrEqualTo(1);
var events = await client.GetFromJsonAsync<List<SbomVersionCreatedEvent>>("/internal/sbom/events");
events.Should().NotBeNull();
events!.Should().HaveCount(1);
events[0].SnapshotId.Should().Be("snap-001");
events[0].TenantId.Should().Be("tenant-a");
// Requesting the projection should not duplicate events.
var projectionResponse = await client.GetAsync("/sboms/snap-001/projection?tenant=tenant-a");
projectionResponse.StatusCode.Should().Be(HttpStatusCode.OK);
var eventsAfterProjection = await client.GetFromJsonAsync<List<SbomVersionCreatedEvent>>("/internal/sbom/events");
eventsAfterProjection.Should().NotBeNull();
eventsAfterProjection!.Should().HaveCount(1);
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace StellaOps.SbomService.Models;
public sealed record SbomVersionCreatedEvent(
string SnapshotId,
string TenantId,
string ProjectionHash,
string SchemaVersion,
DateTimeOffset CreatedAtUtc);

View File

@@ -18,23 +18,10 @@ builder.Services.AddOptions();
builder.Services.AddLogging();
// Register SBOM query services (InMemory seed; replace with Mongo-backed repository later).
builder.Services.AddSingleton<IComponentLookupRepository>(sp =>
{
try
{
var config = sp.GetRequiredService<IConfiguration>();
var mongoConn = config.GetConnectionString("SbomServiceMongo") ?? "mongodb://localhost:27017";
var mongoClient = new MongoDB.Driver.MongoClient(mongoConn);
var databaseName = config.GetSection("SbomService")?["Database"] ?? "sbomservice";
var database = mongoClient.GetDatabase(databaseName);
return new MongoComponentLookupRepository(database);
}
catch
{
// Fallback for test/offline environments when Mongo driver is unavailable.
return new InMemoryComponentLookupRepository();
}
});
builder.Services.AddSingleton<IComponentLookupRepository>(_ => new InMemoryComponentLookupRepository());
builder.Services.AddSingleton<IClock, SystemClock>();
builder.Services.AddSingleton<ISbomEventStore, InMemorySbomEventStore>();
builder.Services.AddSingleton<ISbomEventPublisher>(sp => sp.GetRequiredService<ISbomEventStore>());
builder.Services.AddSingleton<ISbomQueryService, InMemorySbomQueryService>();
builder.Services.AddSingleton<IProjectionRepository>(sp =>
@@ -279,6 +266,39 @@ app.MapGet("/sboms/{snapshotId}/projection", async Task<IResult> (
});
});
app.MapGet("/internal/sbom/events", async Task<IResult> (
[FromServices] ISbomEventStore store,
CancellationToken cancellationToken) =>
{
var events = await store.ListAsync(cancellationToken);
return Results.Ok(events);
});
app.MapPost("/internal/sbom/events/backfill", async Task<IResult> (
[FromServices] IProjectionRepository repository,
[FromServices] ISbomEventPublisher publisher,
[FromServices] IClock clock,
CancellationToken cancellationToken) =>
{
var projections = await repository.ListAsync(cancellationToken);
var published = 0;
foreach (var projection in projections)
{
var evt = new SbomVersionCreatedEvent(
projection.SnapshotId,
projection.TenantId,
projection.ProjectionHash,
projection.SchemaVersion,
clock.UtcNow);
if (await publisher.PublishVersionCreatedAsync(evt, cancellationToken))
{
published++;
}
}
return Results.Ok(new { published });
});
app.Run();
public partial class Program;

View File

@@ -57,6 +57,12 @@ internal sealed class FileProjectionRepository : IProjectionRepository
return Task.FromResult(result);
}
public Task<IReadOnlyList<SbomProjectionResult>> ListAsync(CancellationToken cancellationToken)
{
var list = _projections.Values.ToList();
return Task.FromResult<IReadOnlyList<SbomProjectionResult>>(list);
}
private static string ComputeHash(JsonElement element)
{
var json = JsonSerializer.Serialize(element, new JsonSerializerOptions { WriteIndented = false });

View File

@@ -5,4 +5,5 @@ namespace StellaOps.SbomService.Repositories;
public interface IProjectionRepository
{
Task<SbomProjectionResult?> GetAsync(string snapshotId, string tenantId, CancellationToken cancellationToken);
Task<IReadOnlyList<SbomProjectionResult>> ListAsync(CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,13 @@
using System;
namespace StellaOps.SbomService.Services;
public interface IClock
{
DateTimeOffset UtcNow { get; }
}
public sealed class SystemClock : IClock
{
public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
using System.Globalization;
using StellaOps.SbomService.Models;
using StellaOps.SbomService.Repositories;
using StellaOps.SbomService.Services;
namespace StellaOps.SbomService.Services;
@@ -12,12 +13,20 @@ internal sealed class InMemorySbomQueryService : ISbomQueryService
private readonly IReadOnlyList<CatalogRecord> _catalog;
private readonly IComponentLookupRepository _componentLookupRepository;
private readonly IProjectionRepository _projectionRepository;
private readonly ISbomEventPublisher _eventPublisher;
private readonly IClock _clock;
private readonly ConcurrentDictionary<string, object> _cache = new();
public InMemorySbomQueryService(IComponentLookupRepository componentLookupRepository, IProjectionRepository projectionRepository)
public InMemorySbomQueryService(
IComponentLookupRepository componentLookupRepository,
IProjectionRepository projectionRepository,
ISbomEventPublisher eventPublisher,
IClock clock)
{
_componentLookupRepository = componentLookupRepository;
_projectionRepository = projectionRepository;
_eventPublisher = eventPublisher;
_clock = clock;
// Deterministic seed data for early contract testing; replace with Mongo-backed implementation later.
_paths = SeedPaths();
_timelines = SeedTimelines();
@@ -170,6 +179,13 @@ internal sealed class InMemorySbomQueryService : ISbomQueryService
if (projection is not null)
{
_cache[cacheKey] = projection;
var evt = new SbomVersionCreatedEvent(
projection.SnapshotId,
projection.TenantId,
projection.ProjectionHash,
projection.SchemaVersion,
_clock.UtcNow);
await _eventPublisher.PublishVersionCreatedAsync(evt, cancellationToken);
}
return projection;

View File

@@ -0,0 +1,37 @@
using System.Collections.Concurrent;
using StellaOps.SbomService.Models;
namespace StellaOps.SbomService.Services;
public interface ISbomEventPublisher
{
/// <summary>
/// Publishes a version-created event. Returns true when the event was newly recorded; false when it was already present.
/// </summary>
Task<bool> PublishVersionCreatedAsync(SbomVersionCreatedEvent evt, CancellationToken cancellationToken);
}
public interface ISbomEventStore : ISbomEventPublisher
{
Task<IReadOnlyList<SbomVersionCreatedEvent>> ListAsync(CancellationToken cancellationToken);
}
public sealed class InMemorySbomEventStore : ISbomEventStore
{
private readonly ConcurrentDictionary<string, SbomVersionCreatedEvent> _events = new();
public Task<IReadOnlyList<SbomVersionCreatedEvent>> ListAsync(CancellationToken cancellationToken)
{
var list = _events.Values.OrderBy(e => e.SnapshotId, StringComparer.Ordinal)
.ThenBy(e => e.TenantId, StringComparer.Ordinal)
.ToList();
return Task.FromResult<IReadOnlyList<SbomVersionCreatedEvent>>(list);
}
public Task<bool> PublishVersionCreatedAsync(SbomVersionCreatedEvent evt, CancellationToken cancellationToken)
{
var key = $"{evt.SnapshotId}|{evt.TenantId}|{evt.ProjectionHash}";
var added = _events.TryAdd(key, evt);
return Task.FromResult(added);
}
}

View File

@@ -20,6 +20,6 @@
<ItemGroup>
<ProjectReference Include="..\\__Libraries\\StellaOps.Scanner.Surface.FS\\StellaOps.Scanner.Surface.FS.csproj" />
<ProjectReference Include="..\\__Libraries\\StellaOps.Scanner.Surface.Env\\StellaOps.Scanner.Surface.Env.csproj" />
<PackageReference Include="StellaOps.Scanner.Surface.Env" Version="0.1.0-alpha.20251123" />
</ItemGroup>
</Project>

View File

@@ -32,9 +32,8 @@ public sealed class SurfaceManifestStoreOptionsConfigurator : IConfigureOptions<
if (string.IsNullOrWhiteSpace(options.RootDirectory))
{
options.RootDirectory = Path.Combine(
_cacheOptions.Value.ResolveRoot(),
"manifests");
var cacheRoot = _cacheOptions.Value.RootDirectory ?? Path.Combine(Path.GetTempPath(), "stellaops", "surface-cache");
options.RootDirectory = Path.Combine(cacheRoot, "manifests");
}
}
}

View File

@@ -299,6 +299,18 @@ else
var app = builder.Build();
// Fail fast if surface configuration is invalid at startup.
using (var validationScope = app.Services.CreateScope())
{
var services = validationScope.ServiceProvider;
var env = services.GetRequiredService<ISurfaceEnvironment>();
var runner = services.GetRequiredService<ISurfaceValidatorRunner>();
await runner.EnsureAsync(
SurfaceValidationContext.Create(services, "Scanner.WebService.Startup", env.Settings),
app.Lifetime.ApplicationStopping)
.ConfigureAwait(false);
}
var resolvedOptions = app.Services.GetRequiredService<IOptions<ScannerWebServiceOptions>>().Value;
var authorityConfigured = resolvedOptions.Authority.Enabled;
if (authorityConfigured && resolvedOptions.Authority.AllowAnonymousFallback)

View File

@@ -110,6 +110,22 @@ internal sealed class CompositeScanAnalyzerDispatcher : IScanAnalyzerDispatcher
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
var results = new List<OSPackageAnalyzerResult>(analyzers.Count);
var surfaceEnvironment = services.GetRequiredService<ISurfaceEnvironment>();
var validatorRunner = services.GetRequiredService<ISurfaceValidatorRunner>();
var validationContext = SurfaceValidationContext.Create(
services,
"Scanner.Worker.OSAnalyzers",
surfaceEnvironment.Settings,
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["jobId"] = context.JobId,
["scanId"] = context.ScanId,
["rootfsPath"] = rootfsPath,
["analyzerCount"] = analyzers.Count
});
await validatorRunner.EnsureAsync(validationContext, cancellationToken).ConfigureAwait(false);
foreach (var analyzer in analyzers)
{
cancellationToken.ThrowIfCancellationRequested();

View File

@@ -119,6 +119,20 @@ public sealed class EntryTraceExecutionService : IEntryTraceExecutionService
return;
}
var validationContext = SurfaceValidationContext.Create(
_serviceProvider,
"Scanner.Worker.EntryTrace",
_surfaceEnvironment.Settings,
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["jobId"] = context.JobId,
["scanId"] = context.ScanId,
["configPath"] = configPath,
["rootfs"] = metadata.TryGetValue(_workerOptions.Analyzers.RootFilesystemMetadataKey, out var rootfs) ? rootfs : null
});
await _validatorRunner.EnsureAsync(validationContext, cancellationToken).ConfigureAwait(false);
var fileSystemHandle = BuildFileSystem(context.JobId, metadata);
if (fileSystemHandle is null)
{

View File

@@ -145,6 +145,18 @@ builder.Logging.Configure(options =>
var host = builder.Build();
// Fail fast if surface configuration is invalid at startup.
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var env = services.GetRequiredService<ISurfaceEnvironment>();
var runner = services.GetRequiredService<ISurfaceValidatorRunner>();
await runner.EnsureAsync(
SurfaceValidationContext.Create(services, "Scanner.Worker.Startup", env.Settings),
host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStopping)
.ConfigureAwait(false);
}
await host.RunAsync();
public partial class Program;
@@ -189,9 +201,8 @@ public sealed class SurfaceManifestStoreOptionsConfigurator : IConfigureOptions<
if (string.IsNullOrWhiteSpace(options.RootDirectory))
{
options.RootDirectory = Path.Combine(
_cacheOptions.Value.ResolveRoot(),
"manifests");
var cacheRoot = _cacheOptions.Value.RootDirectory ?? Path.Combine(Path.GetTempPath(), "stellaops", "surface-cache");
options.RootDirectory = Path.Combine(cacheRoot, "manifests");
}
}
}

View File

@@ -29,38 +29,45 @@ public static class ServiceCollectionExtensions
var env = sp.GetRequiredService<ISurfaceEnvironment>();
var options = sp.GetRequiredService<IOptions<SurfaceSecretsOptions>>().Value;
var logger = sp.GetRequiredService<ILoggerFactory>().CreateLogger("SurfaceSecrets");
return CreateProvider(env.Settings.Secrets, logger);
return CreateProviderChain(env.Settings.Secrets, logger);
});
return services;
}
private static ISurfaceSecretProvider CreateProvider(SurfaceSecretsConfiguration configuration, ILogger logger)
private static ISurfaceSecretProvider CreateProviderChain(SurfaceSecretsConfiguration configuration, ILogger logger)
{
var providers = new List<ISurfaceSecretProvider>();
switch (configuration.Provider.ToLowerInvariant())
var providers = new List<ISurfaceSecretProvider>
{
case "kubernetes":
providers.Add(new KubernetesSurfaceSecretProvider(configuration, logger));
break;
case "file":
providers.Add(new FileSurfaceSecretProvider(configuration.Root ?? throw new ArgumentException("Secrets root is required for file provider.")));
break;
case "inline":
providers.Add(new InlineSurfaceSecretProvider(configuration));
break;
default:
logger.LogWarning("Unknown surface secret provider '{Provider}'. Falling back to inline provider.", configuration.Provider);
providers.Add(new InlineSurfaceSecretProvider(configuration));
break;
}
CreateProvider(configuration.Provider, configuration, logger)
};
if (!string.IsNullOrWhiteSpace(configuration.FallbackProvider))
if (configuration.HasFallback)
{
providers.Add(new InlineSurfaceSecretProvider(configuration with { Provider = configuration.FallbackProvider }));
providers.Add(CreateProvider(configuration.FallbackProvider!, configuration, logger));
}
return providers.Count == 1 ? providers[0] : new CompositeSurfaceSecretProvider(providers);
}
private static ISurfaceSecretProvider CreateProvider(string providerId, SurfaceSecretsConfiguration configuration, ILogger logger)
{
if (string.IsNullOrWhiteSpace(providerId))
{
throw new ArgumentException("Provider id is required", nameof(providerId));
}
switch (providerId.Trim().ToLowerInvariant())
{
case "kubernetes":
return new KubernetesSurfaceSecretProvider(configuration, logger);
case "file":
return new FileSurfaceSecretProvider(configuration.Root ?? throw new ArgumentException("Secrets root is required for file provider."));
case "inline":
return new InlineSurfaceSecretProvider(configuration);
default:
logger.LogWarning("Unknown surface secret provider '{Provider}'. Falling back to inline provider if allowed; otherwise requests will fail.", providerId);
return new InlineSurfaceSecretProvider(configuration);
}
}
}

View File

@@ -8,6 +8,7 @@ public static class SurfaceValidationIssueCodes
public const string CacheQuotaInvalid = "SURFACE_ENV_CACHE_QUOTA_INVALID";
public const string SecretsProviderUnknown = "SURFACE_SECRET_PROVIDER_UNKNOWN";
public const string SecretsConfigurationMissing = "SURFACE_SECRET_CONFIGURATION_MISSING";
public const string SecretsConfigurationInvalid = "SURFACE_SECRET_FORMAT_INVALID";
public const string TenantMissing = "SURFACE_ENV_TENANT_MISSING";
public const string BucketMissing = "SURFACE_FS_BUCKET_MISSING";
public const string FeatureUnknown = "SURFACE_FEATURE_UNKNOWN";

View File

@@ -35,6 +35,14 @@ internal sealed class SurfaceSecretsValidator : ISurfaceValidator
"Set SCANNER_SURFACE_SECRETS_PROVIDER to 'kubernetes', 'file', or another supported provider."));
}
if (secrets.HasFallback && !KnownProviders.Contains(secrets.FallbackProvider!))
{
issues.Add(SurfaceValidationIssue.Error(
SurfaceValidationIssueCodes.SecretsProviderUnknown,
$"Fallback secrets provider '{secrets.FallbackProvider}' is not recognised.",
"Choose a supported fallback provider (kubernetes | file | inline) or clear SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER."));
}
if (string.Equals(secrets.Provider, "kubernetes", StringComparison.OrdinalIgnoreCase) &&
string.IsNullOrWhiteSpace(secrets.Namespace))
{
@@ -53,6 +61,24 @@ internal sealed class SurfaceSecretsValidator : ISurfaceValidator
"Set SCANNER_SURFACE_SECRETS_ROOT to a directory path."));
}
if (string.Equals(secrets.Provider, "file", StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrWhiteSpace(secrets.Root) &&
!Directory.Exists(secrets.Root))
{
issues.Add(SurfaceValidationIssue.Error(
SurfaceValidationIssueCodes.SecretsConfigurationInvalid,
$"File secrets root '{secrets.Root}' does not exist.",
"Ensure SCANNER_SURFACE_SECRETS_ROOT points to an existing directory with 0600-style permissions."));
}
if (string.Equals(secrets.Provider, "inline", StringComparison.OrdinalIgnoreCase) && !secrets.AllowInline)
{
issues.Add(SurfaceValidationIssue.Error(
SurfaceValidationIssueCodes.SecretsConfigurationInvalid,
"Inline secrets provider is selected but AllowInline=false.",
"Either enable SCANNER_SURFACE_SECRETS_ALLOW_INLINE for dev/test or switch provider."));
}
if (string.IsNullOrWhiteSpace(secrets.Tenant))
{
issues.Add(SurfaceValidationIssue.Error(

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.Surface.Env;
@@ -23,6 +24,31 @@ namespace StellaOps.Scanner.Surface.Secrets.Tests
Assert.NotNull(secretProvider);
}
[Fact]
public async Task AddSurfaceSecrets_UsesFallbackProvider_WhenPrimaryCannotResolve()
{
const string key = "SURFACE_SECRET_TENANT_COMPONENT_REGISTRY_DEFAULT";
Environment.SetEnvironmentVariable(key, Convert.ToBase64String(new byte[] { 9, 9, 9 }));
var services = new ServiceCollection();
services.AddSingleton<ISurfaceEnvironment>(_ => new TestSurfaceEnvironmentWithFallback());
services.AddLogging(builder => builder.ClearProviders());
services.AddSurfaceSecrets();
await using var provider = services.BuildServiceProvider();
var secretProvider = provider.GetRequiredService<ISurfaceSecretProvider>();
var handle = await secretProvider.GetAsync(new SurfaceSecretRequest("tenant", "component", "registry"));
try
{
Assert.Equal(new byte[] { 9, 9, 9 }, handle.AsBytes().ToArray());
}
finally
{
handle.Dispose();
Environment.SetEnvironmentVariable(key, null);
}
}
private sealed class TestSurfaceEnvironment : ISurfaceEnvironment
{
public SurfaceEnvironmentSettings Settings { get; }
@@ -48,5 +74,32 @@ namespace StellaOps.Scanner.Surface.Secrets.Tests
RawVariables = new Dictionary<string, string>();
}
}
private sealed class TestSurfaceEnvironmentWithFallback : ISurfaceEnvironment
{
public SurfaceEnvironmentSettings Settings { get; }
public IReadOnlyDictionary<string, string> RawVariables { get; }
public TestSurfaceEnvironmentWithFallback()
{
var root = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Settings = new SurfaceEnvironmentSettings(
new Uri("https://surface.example"),
"surface",
null,
new DirectoryInfo(Path.GetTempPath()),
1024,
false,
Array.Empty<string>(),
new SurfaceSecretsConfiguration("kubernetes", "tenant", Root: root, Namespace: "ns", FallbackProvider: "inline", AllowInline: true),
"tenant",
new SurfaceTlsConfiguration(null, null, null))
{
CreatedAtUtc = DateTimeOffset.UtcNow
};
RawVariables = new Dictionary<string, string>();
}
}
}
}

View File

@@ -71,6 +71,63 @@ public sealed class SurfaceValidatorRunnerTests
Assert.True(result.IsSuccess);
}
[Fact]
public async Task RunAllAsync_Fails_WhenInlineProviderDisallowed()
{
var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "stellaops-tests", Guid.NewGuid().ToString()));
var environment = new SurfaceEnvironmentSettings(
new Uri("https://surface.example.com"),
"surface-cache",
null,
directory,
1024,
false,
Array.Empty<string>(),
new SurfaceSecretsConfiguration("inline", "tenant-a", Root: null, Namespace: null, FallbackProvider: null, AllowInline: false),
"tenant-a",
new SurfaceTlsConfiguration(null, null, null));
var services = CreateServices();
var runner = services.GetRequiredService<ISurfaceValidatorRunner>();
var context = SurfaceValidationContext.Create(services, "TestComponent", environment);
var result = await runner.RunAllAsync(context);
Assert.False(result.IsSuccess);
Assert.Contains(result.Issues, i => i.Code == SurfaceValidationIssueCodes.SecretsConfigurationInvalid);
}
[Fact]
public async Task RunAllAsync_Fails_WhenFileRootMissing()
{
var missingRoot = Path.Combine(Path.GetTempPath(), "stellaops-tests", "missing-root", Guid.NewGuid().ToString());
var directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "stellaops-tests", Guid.NewGuid().ToString()))
{
Attributes = FileAttributes.Normal
};
var environment = new SurfaceEnvironmentSettings(
new Uri("https://surface.example.com"),
"surface-cache",
null,
directory,
1024,
false,
Array.Empty<string>(),
new SurfaceSecretsConfiguration("file", "tenant-a", Root: missingRoot, Namespace: null, FallbackProvider: null, AllowInline: false),
"tenant-a",
new SurfaceTlsConfiguration(null, null, null));
var services = CreateServices();
var runner = services.GetRequiredService<ISurfaceValidatorRunner>();
var context = SurfaceValidationContext.Create(services, "TestComponent", environment);
var result = await runner.RunAllAsync(context);
Assert.False(result.IsSuccess);
Assert.Contains(result.Issues, i => i.Code == SurfaceValidationIssueCodes.SecretsConfigurationInvalid);
}
private static ServiceProvider CreateServices(Action<IServiceCollection>? configure = null)
{
var services = new ServiceCollection();

View File

@@ -5,7 +5,6 @@ using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Options;
using StellaOps.Scanner.Surface.Env;
using StellaOps.Scanner.Surface.FS;
using StellaOps.Scanner.Worker;
using Xunit;
namespace StellaOps.Scanner.Worker.Tests;
@@ -29,7 +28,7 @@ public sealed class SurfaceManifestStoreOptionsConfiguratorTests
new SurfaceTlsConfiguration(null, null, new X509Certificate2Collection()));
var environment = new StubSurfaceEnvironment(settings);
var cacheOptions = Options.Create(new SurfaceCacheOptions { RootDirectory = cacheRoot.FullName });
var cacheOptions = Microsoft.Extensions.Options.Options.Create(new SurfaceCacheOptions { RootDirectory = cacheRoot.FullName });
var configurator = new SurfaceManifestStoreOptionsConfigurator(environment, cacheOptions);
var options = new SurfaceManifestStoreOptions();