Add tests and implement StubBearer authentication for Signer endpoints
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Created SignerEndpointsTests to validate the SignDsse and VerifyReferrers endpoints.
- Implemented StubBearerAuthenticationDefaults and StubBearerAuthenticationHandler for token-based authentication.
- Developed ConcelierExporterClient for managing Trivy DB settings and export operations.
- Added TrivyDbSettingsPageComponent for UI interactions with Trivy DB settings, including form handling and export triggering.
- Implemented styles and HTML structure for Trivy DB settings page.
- Created NotifySmokeCheck tool for validating Redis event streams and Notify deliveries.
This commit is contained in:
2025-10-21 09:37:07 +03:00
parent 2b6304c9c3
commit 791e12baab
298 changed files with 20490 additions and 5751 deletions

View File

@@ -539,9 +539,53 @@ PY
echo " Service path: ${{ steps.params.outputs.path || '(skipped)' }}"
echo " Docs path: ${{ steps.params.outputs['docs-path'] || '(skipped)' }}"
- name: Deployment skipped summary
if: steps.check-deploy.outputs.should-deploy != 'true'
run: |
echo " Deployment stage skipped"
echo " Event: ${{ github.event_name }}"
echo " Ref: ${{ github.ref }}"
- name: Deployment skipped summary
if: steps.check-deploy.outputs.should-deploy != 'true'
run: |
echo " Deployment stage skipped"
echo " Event: ${{ github.event_name }}"
echo " Ref: ${{ github.ref }}"
notify-smoke:
runs-on: ubuntu-22.04
needs: deploy
if: needs.deploy.result == 'success'
env:
DOTNET_VERSION: ${{ env.DOTNET_VERSION }}
NOTIFY_SMOKE_REDIS_DSN: ${{ secrets.NOTIFY_SMOKE_REDIS_DSN }}
NOTIFY_SMOKE_NOTIFY_BASEURL: ${{ secrets.NOTIFY_SMOKE_NOTIFY_BASEURL }}
NOTIFY_SMOKE_NOTIFY_TOKEN: ${{ secrets.NOTIFY_SMOKE_NOTIFY_TOKEN }}
NOTIFY_SMOKE_NOTIFY_TENANT: ${{ secrets.NOTIFY_SMOKE_NOTIFY_TENANT }}
NOTIFY_SMOKE_NOTIFY_TENANT_HEADER: ${{ secrets.NOTIFY_SMOKE_NOTIFY_TENANT_HEADER }}
NOTIFY_SMOKE_EXPECT_KINDS: ${{ vars.NOTIFY_SMOKE_EXPECT_KINDS || secrets.NOTIFY_SMOKE_EXPECT_KINDS }}
NOTIFY_SMOKE_LOOKBACK_MINUTES: ${{ vars.NOTIFY_SMOKE_LOOKBACK_MINUTES || secrets.NOTIFY_SMOKE_LOOKBACK_MINUTES }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET ${{ env.DOTNET_VERSION }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
include-prerelease: true
- name: Validate Notify smoke configuration
run: |
missing=()
for name in NOTIFY_SMOKE_REDIS_DSN NOTIFY_SMOKE_NOTIFY_BASEURL NOTIFY_SMOKE_NOTIFY_TOKEN NOTIFY_SMOKE_NOTIFY_TENANT NOTIFY_SMOKE_EXPECT_KINDS NOTIFY_SMOKE_LOOKBACK_MINUTES
do
value="${!name}"
if [ -z "$value" ]; then
missing+=("$name")
fi
done
if [ ${#missing[@]} -gt 0 ]; then
echo "❌ Missing Notify smoke configuration: ${missing[*]}"
exit 1
fi
- name: Restore Notify smoke checker
run: dotnet restore tools/NotifySmokeCheck/NotifySmokeCheck.csproj
- name: Run Notify smoke validation
run: dotnet run --project tools/NotifySmokeCheck/NotifySmokeCheck.csproj --configuration Release

View File

@@ -4,7 +4,7 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
## Wave Instructions
### Wave 0
- Team Attestor Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Attestor/TASKS.md`. Focus on ATTESTOR-API-11-201 (TODO), ATTESTOR-VERIFY-11-202 (TODO), ATTESTOR-OBS-11-203 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Authority Core & Security Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Authority/TASKS.md`. Focus on AUTH-DPOP-11-001 (DOING 2025-10-19), AUTH-MTLS-11-002 (DOING 2025-10-19). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Authority Core & Security Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Authority/TASKS.md`. Focus on AUTH-DPOP-11-001 (DONE 2025-10-20), AUTH-MTLS-11-002 (DOING 2025-10-19). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Authority Core & Storage Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Authority/TASKS.md`. Focus on AUTHSTORAGE-MONGO-08-001 (DONE 2025-10-19). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team DevEx/CLI: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Cli/TASKS.md`. Focus on EXCITITOR-CLI-01-002 (TODO), CLI-RUNTIME-13-005 (TODO). Confirm prerequisites (external: EXCITITOR-CLI-01-001, EXCITITOR-EXPORT-01-001) before starting and report status in module TASKS.md.
- Team DevOps Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `ops/devops/TASKS.md`. Focus on DEVOPS-SEC-10-301 (DONE 2025-10-20); Wave0A prerequisites reconfirmed so remediation work may proceed. Keep module TASKS.md/Sprints in sync as patches land.
@@ -18,17 +18,18 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Team Notify Storage Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Notify.Storage.Mongo/TASKS.md`. Focus on NOTIFY-STORAGE-15-201 (TODO), NOTIFY-STORAGE-15-202 (TODO), NOTIFY-STORAGE-15-203 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Notify WebService Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Notify.WebService/TASKS.md`. Focus on NOTIFY-WEB-15-101 (TODO), NOTIFY-WEB-15-102 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Platform Events Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `docs/TASKS.md`. Focus on PLATFORM-EVENTS-09-401 (TODO). Confirm prerequisites (external: DOCS-EVENTS-09-003) before starting and report status in module TASKS.md.
- Team Plugin Platform Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Plugin/TASKS.md`. Focus on PLUGIN-DI-08-001 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Plugin Platform Guild, Authority Core: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Plugin/TASKS.md`. Focus on PLUGIN-DI-08-002 (TODO); coordination session booked for 2025-10-20 to unblock implementation. Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Policy Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Policy/TASKS.md`. Focus on POLICY-CORE-09-004 (TODO), POLICY-CORE-09-005 (TODO), POLICY-CORE-09-006 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Plugin Platform Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Plugin/TASKS.md`. Focus on PLUGIN-DI-08-002.COORD (DONE 2025-10-20), PLUGIN-DI-08-002 (DONE 2025-10-20), PLUGIN-DI-08-003 (DONE 2025-10-20), PLUGIN-DI-08-004 (DONE 2025-10-20), and PLUGIN-DI-08-005 (DONE 2025-10-20). Confirm prerequisites (PLUGIN-DI-08-001) before starting and report status in module TASKS.md.
- Team Plugin Platform Guild, Authority Core: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Plugin/TASKS.md`. Coordination session for PLUGIN-DI-08-002 implementation completed on 2025-10-20 15:0016:05UTC and scoped-service changes have shipped with regression coverage; subsequent tasks (PLUGIN-DI-08-003/004/005) remain green.
- Team Policy Guild: Sprint9 core tasks (POLICY-CORE-09-004/005/006) closed on 2025-10-19; ensure downstream consumers refresh against the published scoring config + quiet/unknown outputs and raise follow-up tasks if additional polish is required.
- Team Runtime Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `docs/TASKS.md`. Focus on RUNTIME-GUILD-09-402 (TODO). Confirm prerequisites (external: SCANNER-POLICY-09-107) before starting and report status in module TASKS.md.
- Team Scanner WebService Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. Focus on SCANNER-EVENTS-15-201 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Scheduler ImpactIndex Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.ImpactIndex/TASKS.md`. Focus on SCHED-IMPACT-16-300 (DOING). Confirm prerequisites (external: SAMPLES-10-001) before starting and report status in module TASKS.md.
- Team Scheduler Models Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.Models/TASKS.md`. Focus on SCHED-MODELS-16-103 (TODO). Confirm prerequisites (external: SCHED-MODELS-16-101) before starting and report status in module TASKS.md.
- Team Scheduler Queue Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.Queue/TASKS.md`. Focus on SCHED-QUEUE-16-401 (TODO). Confirm prerequisites (external: SCHED-MODELS-16-101) before starting and report status in module TASKS.md.
- Team Scanner WebService Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. Focus on SCANNER-EVENTS-15-201 (DONE 2025-10-20). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Scanner WebService Guild: read EXECPLAN.md Wave 2 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. Focus on SCANNER-EVENTS-16-301 (BLOCKED 2025-10-20). Wait for NOTIFY-QUEUE-15-401 before attempting integration.
- Team Scheduler ImpactIndex Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.ImpactIndex/TASKS.md`. Focus on SCHED-IMPACT-16-300 (DONE 2025-10-20) and ensure the temporary stub removal note stays tracked. Confirm prerequisites (external: SAMPLES-10-001) before starting and report status in module TASKS.md.
- Team Scheduler Models Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.Models/TASKS.md`. SCHED-MODELS-16-103 completed (2025-10-20); ensure downstream teams consume the migration helpers and log upgrade warnings.
- Team Scheduler Queue Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.Queue/TASKS.md`. SCHED-QUEUE-16-401 completed (2025-10-20); proceed with Wave 1 queue enhancements.
- Team Scheduler Storage Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.Storage.Mongo/TASKS.md`. Focus on SCHED-STORAGE-16-201 (TODO). Confirm prerequisites (external: SCHED-MODELS-16-101) before starting and report status in module TASKS.md.
- Team Scheduler WebService Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scheduler.WebService/TASKS.md`. Focus on SCHED-WEB-16-101 (TODO). Confirm prerequisites (external: SCHED-MODELS-16-101) before starting and report status in module TASKS.md.
- Team Signer Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Signer/TASKS.md`. Focus on SIGNER-API-11-101 (TODO), SIGNER-REF-11-102 (TODO), SIGNER-QUOTA-11-103 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Signer Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Signer/TASKS.md`. Focus on SIGNER-API-11-101 (DONE 2025-10-21), SIGNER-REF-11-102 (DONE 2025-10-21), SIGNER-QUOTA-11-103 (DONE 2025-10-21). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team TBD: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.Node/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-302C (TODO). Confirm prerequisites (external: SCANNER-ANALYZERS-LANG-10-302B) before starting and report status in module TASKS.md.
- Team Team Connector Resumption CERT/RedHat: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.Distro.RedHat/TASKS.md`. Focus on FEEDCONN-REDHAT-02-001 (DOING). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Team Excititor Attestation: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.Attestation/TASKS.md`. Focus on EXCITITOR-ATTEST-01-003 (TODO). Confirm prerequisites (external: EXCITITOR-ATTEST-01-002) before starting and report status in module TASKS.md.
@@ -40,13 +41,13 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Team Team Excititor Export: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.Export/TASKS.md`. Focus on EXCITITOR-EXPORT-01-005 (TODO). Confirm prerequisites (external: EXCITITOR-CORE-02-001, EXCITITOR-EXPORT-01-004) before starting and report status in module TASKS.md.
- Team Team Excititor Formats: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.Formats.CSAF/TASKS.md`, `src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md`, `src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md`. Focus on EXCITITOR-FMT-CSAF-01-002 (TODO), EXCITITOR-FMT-CSAF-01-003 (TODO), EXCITITOR-FMT-CYCLONE-01-002 (TODO), EXCITITOR-FMT-CYCLONE-01-003 (TODO), EXCITITOR-FMT-OPENVEX-01-002 (TODO), EXCITITOR-FMT-OPENVEX-01-003 (TODO). Confirm prerequisites (external: EXCITITOR-EXPORT-01-001, EXCITITOR-FMT-CSAF-01-001, EXCITITOR-FMT-CYCLONE-01-001, EXCITITOR-FMT-OPENVEX-01-001, EXCITITOR-POLICY-01-001) before starting and report status in module TASKS.md.
- Team Team Excititor Storage: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.Storage.Mongo/TASKS.md`. Focus on EXCITITOR-STORAGE-MONGO-08-001 (DONE 2025-10-19), EXCITITOR-STORAGE-03-001 (TODO). Confirm prerequisites (external: EXCITITOR-STORAGE-01-003, EXCITITOR-STORAGE-02-001) before starting and report status in module TASKS.md.
- Team Team Excititor WebService: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.WebService/TASKS.md`. Focus on EXCITITOR-WEB-01-002 (TODO), EXCITITOR-WEB-01-003 (TODO), EXCITITOR-WEB-01-004 (TODO). Confirm prerequisites (external: EXCITITOR-ATTEST-01-001, EXCITITOR-EXPORT-01-001, EXCITITOR-WEB-01-001) before starting and report status in module TASKS.md.
- Team Team Excititor Worker: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.Worker/TASKS.md`. Focus on EXCITITOR-WORKER-01-002 (TODO), EXCITITOR-WORKER-01-004 (TODO), EXCITITOR-WORKER-02-001 (TODO). Confirm prerequisites (external: EXCITITOR-CORE-02-001, EXCITITOR-WORKER-01-001) before starting and report status in module TASKS.md.
- Team Team Merge & QA Enforcement: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Concelier.Merge/TASKS.md`. Focus on FEEDMERGE-COORD-02-900 (DOING). Confirm prerequisites (none) before starting and report status in module TASKS.md. **2025-10-19:** Coordination refreshed; connector owners notified and TASKS.md entries updated.
- Team Team Excititor WebService: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.WebService/TASKS.md`. Focus on EXCITITOR-WEB-01-002 (DONE 2025-10-20), EXCITITOR-WEB-01-003 (TODO), EXCITITOR-WEB-01-004 (DONE 2025-10-20). Confirm prerequisites (external: EXCITITOR-ATTEST-01-001, EXCITITOR-EXPORT-01-001, EXCITITOR-WEB-01-001) before starting and report status in module TASKS.md.
- Team Team Excititor Worker: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Excititor.Worker/TASKS.md`. Focus on EXCITITOR-WORKER-01-004 (DONE 2025-10-21); EXCITITOR-WORKER-01-002 (DONE 2025-10-21) and EXCITITOR-WORKER-02-001 (DONE 2025-10-21) recorded. Confirm prerequisites (external: EXCITITOR-CORE-02-001, EXCITITOR-WORKER-01-001) before starting and report status in module TASKS.md.
- Team Team Merge & QA Enforcement: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Concelier.Merge/TASKS.md`. Focus on FEEDMERGE-COORD-02-900 (DOING). Confirm prerequisites (none) before starting and report status in module TASKS.md. **2025-10-19:** Coordination refreshed; connector owners notified and TASKS.md entries updated. **2025-10-20:** Coordination matrix + rollout dashboard refreshed with connector due dates (Cccs/Cisco 2025-10-21, CertBund 2025-10-22, ICS-CISA 2025-10-23, KISA 2025-10-24) and escalation plan logged.
- Team Team Normalization & Storage Backbone: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Concelier.Storage.Mongo/TASKS.md`. Focus on FEEDSTORAGE-MONGO-08-001 (DONE 2025-10-19). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Team WebService & Authority: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md`, `src/StellaOps.Concelier.WebService/TASKS.md`. Focus on SEC2.PLG (DOING), SEC3.PLG (DOING), SEC5.PLG (DOING), PLG4-6.CAPABILITIES (BLOCKED), PLG6.DIAGRAM (TODO), PLG7.RFC (REVIEW), FEEDWEB-DOCS-01-001 (DOING), FEEDWEB-OPS-01-006 (TODO), FEEDWEB-OPS-01-007 (BLOCKED). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Tools Guild, BE-Conn-MSRC: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.Common/TASKS.md`. Focus on FEEDCONN-SHARED-STATE-003 (**TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team UX Specialist, Angular Eng: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Web/TASKS.md`. Focus on WEB1.TRIVY-SETTINGS (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team UX Specialist, Angular Eng: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Web/TASKS.md`. Focus on WEB1.TRIVY-SETTINGS (DONE 2025-10-21) and WEB1.TRIVY-SETTINGS-TESTS (BLOCKED 2025-10-21). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Zastava Core Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Zastava.Core/TASKS.md`. Focus on ZASTAVA-CORE-12-201 (TODO), ZASTAVA-CORE-12-202 (TODO), ZASTAVA-CORE-12-203 (TODO), ZASTAVA-OPS-12-204 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
- Team Zastava Webhook Guild: read EXECPLAN.md Wave 0 and SPRINTS.md rows for `src/StellaOps.Zastava.Webhook/TASKS.md`. Focus on ZASTAVA-WEBHOOK-12-101 (TODO), ZASTAVA-WEBHOOK-12-102 (TODO), ZASTAVA-WEBHOOK-12-103 (TODO). Confirm prerequisites (none) before starting and report status in module TASKS.md.
@@ -61,9 +62,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Team Notify Engine Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Notify.Engine/TASKS.md`. Focus on NOTIFY-ENGINE-15-301 (TODO). Confirm prerequisites (internal: NOTIFY-MODELS-15-101 (Wave 0)) before starting and report status in module TASKS.md.
- Team Notify Queue Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Notify.Queue/TASKS.md`. Focus on NOTIFY-QUEUE-15-401 (TODO). Confirm prerequisites (internal: NOTIFY-MODELS-15-101 (Wave 0)) before starting and report status in module TASKS.md.
- Team Notify WebService Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Notify.WebService/TASKS.md`. Focus on NOTIFY-WEB-15-103 (DONE). Confirm prerequisites (internal: NOTIFY-WEB-15-102 (Wave 0)) before starting and report status in module TASKS.md.
- Team Scanner WebService Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. Focus on SCANNER-RUNTIME-12-301 (TODO). Confirm prerequisites (internal: ZASTAVA-CORE-12-201 (Wave 0)) before starting and report status in module TASKS.md.
- Team Scanner WebService Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. SCANNER-RUNTIME-12-301 closed (2025-10-20); coordinate with Zastava observer guild on batch fixtures and advance to SCANNER-RUNTIME-12-302.
- Team Scheduler ImpactIndex Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scheduler.ImpactIndex/TASKS.md`. Focus on SCHED-IMPACT-16-301 (TODO). Confirm prerequisites (internal: SCANNER-EMIT-10-605 (Wave 0)) before starting and report status in module TASKS.md.
- Team Scheduler Queue Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scheduler.Queue/TASKS.md`. Focus on SCHED-QUEUE-16-402 (TODO), SCHED-QUEUE-16-403 (TODO). Confirm prerequisites (internal: SCHED-QUEUE-16-401 (Wave 0)) before starting and report status in module TASKS.md.
- Team Scheduler Queue Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scheduler.Queue/TASKS.md`. SCHED-QUEUE-16-402 completed (2025-10-20); next focus is SCHED-QUEUE-16-403.
- Team Scheduler Storage Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scheduler.Storage.Mongo/TASKS.md`. Focus on SCHED-STORAGE-16-203 (TODO), SCHED-STORAGE-16-202 (TODO). Confirm prerequisites (internal: SCHED-STORAGE-16-201 (Wave 0)) before starting and report status in module TASKS.md.
- Team Scheduler WebService Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scheduler.WebService/TASKS.md`. Focus on SCHED-WEB-16-104 (TODO), SCHED-WEB-16-102 (TODO). Confirm prerequisites (internal: SCHED-QUEUE-16-401 (Wave 0), SCHED-STORAGE-16-201 (Wave 0), SCHED-WEB-16-101 (Wave 0)) before starting and report status in module TASKS.md.
- Team Scheduler Worker Guild: read EXECPLAN.md Wave 1 and SPRINTS.md rows for `src/StellaOps.Scheduler.Worker/TASKS.md`. Focus on SCHED-WORKER-16-201 (TODO). Confirm prerequisites (internal: SCHED-QUEUE-16-401 (Wave 0)) before starting and report status in module TASKS.md.
@@ -99,7 +100,7 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Team Zastava Observer Guild: read EXECPLAN.md Wave 2 and SPRINTS.md rows for `src/StellaOps.Zastava.Observer/TASKS.md`. Focus on ZASTAVA-OBS-12-002 (TODO). Confirm prerequisites (internal: ZASTAVA-OBS-12-001 (Wave 1)) before starting and report status in module TASKS.md.
### Wave 3
- Team DevEx/CLI: read EXECPLAN.md Wave 3 and SPRINTS.md rows for `src/StellaOps.Cli/TASKS.md`. Focus on CLI-OFFLINE-13-006 (TODO). Confirm prerequisites (internal: DEVOPS-OFFLINE-14-002 (Wave 2)) before starting and report status in module TASKS.md.
- Team DevEx/CLI: read EXECPLAN.md Wave 3 and SPRINTS.md rows for `src/StellaOps.Cli/TASKS.md`. Focus on CLI-OFFLINE-13-006 (DONE 2025-10-21). Confirm prerequisites (internal: DEVOPS-OFFLINE-14-002 (Wave 2)) before starting and report status in module TASKS.md.
- Team DevEx/CLI, Scanner WebService Guild: read EXECPLAN.md Wave 3 and SPRINTS.md rows for `src/StellaOps.Cli/TASKS.md`. Focus on CLI-RUNTIME-13-008 (TODO). Confirm prerequisites (internal: SCANNER-RUNTIME-12-302 (Wave 2)) before starting and report status in module TASKS.md.
- Team Excititor Connectors Stella: read EXECPLAN.md Wave 3 and SPRINTS.md rows for `src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md`. Focus on EXCITITOR-CONN-STELLA-07-001 (TODO). Confirm prerequisites (internal: EXCITITOR-EXPORT-01-007 (Wave 2)) before starting and report status in module TASKS.md.
- Team Notify Engine Guild: read EXECPLAN.md Wave 3 and SPRINTS.md rows for `src/StellaOps.Notify.Engine/TASKS.md`. Focus on NOTIFY-ENGINE-15-303 (TODO). Confirm prerequisites (internal: NOTIFY-ENGINE-15-302 (Wave 2)) before starting and report status in module TASKS.md.
@@ -121,19 +122,19 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
### Wave 5
- Team Excititor Connectors Stella: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md`. Focus on EXCITITOR-CONN-STELLA-07-003 (TODO). Confirm prerequisites (internal: EXCITITOR-CONN-STELLA-07-002 (Wave 4)) before starting and report status in module TASKS.md.
- Team Notify Connectors Guild: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Notify.Connectors.Email/TASKS.md`, `src/StellaOps.Notify.Connectors.Slack/TASKS.md`, `src/StellaOps.Notify.Connectors.Teams/TASKS.md`, `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`. Focus on NOTIFY-CONN-SLACK-15-502 (DOING), NOTIFY-CONN-TEAMS-15-602 (DOING), NOTIFY-CONN-EMAIL-15-702 (DOING), NOTIFY-CONN-WEBHOOK-15-802 (DOING). Confirm prerequisites (internal: NOTIFY-CONN-EMAIL-15-701 (Wave 4), NOTIFY-CONN-SLACK-15-501 (Wave 4), NOTIFY-CONN-TEAMS-15-601 (Wave 4), NOTIFY-CONN-WEBHOOK-15-801 (Wave 4)) before starting and report status in module TASKS.md.
- Team Notify Connectors Guild: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Notify.Connectors.Email/TASKS.md`, `src/StellaOps.Notify.Connectors.Slack/TASKS.md`, `src/StellaOps.Notify.Connectors.Teams/TASKS.md`, `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`. Focus on NOTIFY-CONN-SLACK-15-502 (DONE), NOTIFY-CONN-TEAMS-15-602 (DONE), NOTIFY-CONN-EMAIL-15-702 (BLOCKED 2025-10-20), NOTIFY-CONN-WEBHOOK-15-802 (BLOCKED 2025-10-20). Confirm prerequisites (internal: NOTIFY-CONN-EMAIL-15-701 (Wave 4), NOTIFY-CONN-SLACK-15-501 (Wave 4), NOTIFY-CONN-TEAMS-15-601 (Wave 4), NOTIFY-CONN-WEBHOOK-15-801 (Wave 4)) before starting and report status in module TASKS.md.
- Team Scanner WebService Guild: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. Focus on SCANNER-RUNTIME-17-401 (TODO). Confirm prerequisites (internal: POLICY-RUNTIME-17-201 (Wave 4), SCANNER-EMIT-17-701 (Wave 1), SCANNER-RUNTIME-12-301 (Wave 1), ZASTAVA-OBS-17-005 (Wave 3)) before starting and report status in module TASKS.md.
- Team TBD: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-308D (TODO), SCANNER-ANALYZERS-LANG-10-308G (TODO), SCANNER-ANALYZERS-LANG-10-308P (TODO), SCANNER-ANALYZERS-LANG-10-308R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-307D (Wave 4), SCANNER-ANALYZERS-LANG-10-307G (Wave 4), SCANNER-ANALYZERS-LANG-10-307P (Wave 4), SCANNER-ANALYZERS-LANG-10-307R (Wave 4)) before starting and report status in module TASKS.md.
### Wave 6
- Team Notify Connectors Guild: read EXECPLAN.md Wave 6 and SPRINTS.md rows for `src/StellaOps.Notify.Connectors.Email/TASKS.md`, `src/StellaOps.Notify.Connectors.Slack/TASKS.md`, `src/StellaOps.Notify.Connectors.Teams/TASKS.md`, `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`. Focus on NOTIFY-CONN-SLACK-15-503 (TODO), NOTIFY-CONN-TEAMS-15-603 (TODO), NOTIFY-CONN-EMAIL-15-703 (TODO), NOTIFY-CONN-WEBHOOK-15-803 (TODO). Confirm prerequisites (internal: NOTIFY-CONN-EMAIL-15-702 (Wave 5), NOTIFY-CONN-SLACK-15-502 (Wave 5), NOTIFY-CONN-TEAMS-15-602 (Wave 5), NOTIFY-CONN-WEBHOOK-15-802 (Wave 5)) before starting and report status in module TASKS.md.
- Team Notify Connectors Guild: read EXECPLAN.md Wave 6 and SPRINTS.md rows for `src/StellaOps.Notify.Connectors.Email/TASKS.md`, `src/StellaOps.Notify.Connectors.Slack/TASKS.md`, `src/StellaOps.Notify.Connectors.Teams/TASKS.md`, `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`. Focus on NOTIFY-CONN-SLACK-15-503 (DONE), NOTIFY-CONN-TEAMS-15-603 (DONE), NOTIFY-CONN-EMAIL-15-703 (DONE), NOTIFY-CONN-WEBHOOK-15-803 (DONE). Confirm packaging outputs remain deterministic while upstream implementation tasks (15-702/802) stay blocked.
- Team TBD: read EXECPLAN.md Wave 6 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-309D (TODO), SCANNER-ANALYZERS-LANG-10-309G (TODO), SCANNER-ANALYZERS-LANG-10-309P (TODO), SCANNER-ANALYZERS-LANG-10-309R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-308D (Wave 5), SCANNER-ANALYZERS-LANG-10-308G (Wave 5), SCANNER-ANALYZERS-LANG-10-308P (Wave 5), SCANNER-ANALYZERS-LANG-10-308R (Wave 5)) before starting and report status in module TASKS.md.
### Wave 7
- Team Team Core Engine & Storage Analytics: read EXECPLAN.md Wave 7 and SPRINTS.md rows for `src/StellaOps.Concelier.Core/TASKS.md`. Focus on FEEDCORE-ENGINE-07-001 (DONE 2025-10-19). Confirm prerequisites (internal: FEEDSTORAGE-DATA-07-001 (Wave 10)) before starting and report status in module TASKS.md.
### Wave 8
- Team Team Core Engine & Data Science: read EXECPLAN.md Wave 8 and SPRINTS.md rows for `src/StellaOps.Concelier.Core/TASKS.md`. Focus on FEEDCORE-ENGINE-07-002 (TODO). Confirm prerequisites (internal: FEEDCORE-ENGINE-07-001 (Wave 7)) before starting and report status in module TASKS.md.
- Team Team Core Engine & Data Science: read EXECPLAN.md Wave 8 and SPRINTS.md rows for `src/StellaOps.Concelier.Core/TASKS.md`. Focus on FEEDCORE-ENGINE-07-002 (DONE 2025-10-21). Confirm prerequisites (internal: FEEDCORE-ENGINE-07-001 (Wave 7)) before starting and report status in module TASKS.md.
### Wave 9
- Team Team Core Engine & Storage Analytics: read EXECPLAN.md Wave 9 and SPRINTS.md rows for `src/StellaOps.Concelier.Core/TASKS.md`. Focus on FEEDCORE-ENGINE-07-003 (TODO). Confirm prerequisites (internal: FEEDCORE-ENGINE-07-001 (Wave 7)) before starting and report status in module TASKS.md.
@@ -154,10 +155,10 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Team Concelier WebService Guild: read EXECPLAN.md Wave 14 and SPRINTS.md rows for `src/StellaOps.Concelier.WebService/TASKS.md`. CONCELIER-WEB-08-201 closed (2025-10-20); coordinate with DevOps for mirror smoke before promoting to stable.
### Wave 15
- Team BE-Conn-Stella: read EXECPLAN.md Wave 15 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`. Focus on FEEDCONN-STELLA-08-001 (TODO). Confirm prerequisites (internal: CONCELIER-EXPORT-08-201 (Wave 12)) before starting and report status in module TASKS.md.
- Team BE-Conn-Stella: read EXECPLAN.md Wave 15 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`. Focus on FEEDCONN-STELLA-08-001 (DONE 2025-10-20). Confirm prerequisites (internal: CONCELIER-EXPORT-08-201 (Wave 12)) before starting and report status in module TASKS.md.
### Wave 16
- Team BE-Conn-Stella: read EXECPLAN.md Wave 16 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`. Focus on FEEDCONN-STELLA-08-002 (TODO). Confirm prerequisites (internal: FEEDCONN-STELLA-08-001 (Wave 15)) before starting and report status in module TASKS.md.
- Team BE-Conn-Stella: read EXECPLAN.md Wave 16 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`. FEEDCONN-STELLA-08-002 completed (2025-10-20) with canonical DTO mapper + provenance fixtures.
### Wave 17
- Team BE-Conn-Stella: read EXECPLAN.md Wave 17 and SPRINTS.md rows for `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`. Focus on FEEDCONN-STELLA-08-003 (TODO). Confirm prerequisites (internal: FEEDCONN-STELLA-08-002 (Wave 16)) before starting and report status in module TASKS.md.
@@ -166,9 +167,12 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 1** · Backlog
- Team: UX Specialist, Angular Eng
- Path: `src/StellaOps.Web/TASKS.md`
1. [TODO] WEB1.TRIVY-SETTINGS — Implement Trivy DB exporter settings panel with `publishFull`, `publishDelta`, `includeFull`, `includeDelta` toggles and “Run export now” action using future `/exporters/trivy-db/settings` API.
1. [DONE] WEB1.TRIVY-SETTINGS — Implement Trivy DB exporter settings panel with `publishFull`, `publishDelta`, `includeFull`, `includeDelta` toggles and “Run export now” action using future `/exporters/trivy-db/settings` API.
• Prereqs: —
• Current: TODO
• Current: DONE (2025-10-21) Angular route `/concelier/trivy-db-settings` with reactive form, API client, and run-now workflow built; see `TrivyDbSettingsPageComponent`.
2. [BLOCKED] WEB1.TRIVY-SETTINGS-TESTS — Add headless UI test run (`ng test --watch=false`) and document prerequisites once Angular tooling is chained up.
• Prereqs: WEB1.TRIVY-SETTINGS
• Current: BLOCKED (2025-10-21) Awaiting Angular CLI/toolchain availability in CI/local dev environments before wiring Karma tests for the new screen.
- **Sprint 1** · Developer Tooling
- Team: DevEx/CLI
- Path: `src/StellaOps.Cli/TASKS.md`
@@ -226,7 +230,7 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Path: `src/StellaOps.Concelier.Merge/TASKS.md`
1. [DOING] FEEDMERGE-COORD-02-900 — Range primitives rollout coordination — Coordinate remaining connectors (`Acsc`, `Cccs`, `CertBund`, `CertCc`, `Cve`, `Ghsa`, `Ics.Cisa`, `Kisa`, `Ru.Bdu`, `Ru.Nkcki`, `Vndr.Apple`, `Vndr.Cisco`, `Vndr.Msrc`) to emit canonical range primitives with provenance tags; fixtures tracked in `RANGE_PRIMITIVES_COORDINATION.md`.
• Prereqs: —
• Current: DOING (2025-10-12)
• Current: DOING (2025-10-20) Coordination docs refreshed with connector due dates (Cccs/Cisco 2025-10-21, CertBund 2025-10-22, ICS-CISA 2025-10-23, KISA 2025-10-24); escalation plan defined if deadlines slip.
- **Sprint 3** · Backlog
- Team: Tools Guild, BE-Conn-MSRC
- Path: `src/StellaOps.Concelier.Connector.Common/TASKS.md`
@@ -241,9 +245,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO Add verification helpers for Worker/WebService, metrics/logging hooks, and negative-path regression tests.
- Team: Team Excititor WebService
- Path: `src/StellaOps.Excititor.WebService/TASKS.md`
1. [TODO] EXCITITOR-WEB-01-002 — EXCITITOR-WEB-01-002 Ingest & reconcile endpoints
1. [DONE] EXCITITOR-WEB-01-002 — EXCITITOR-WEB-01-002 Ingest & reconcile endpoints
• Prereqs: EXCITITOR-WEB-01-001 (external/completed)
• Current: TODO Implement `/excititor/init`, `/excititor/ingest/run`, `/excititor/ingest/resume`, `/excititor/reconcile` with token scope enforcement and structured run telemetry.
• Current: DONE (2025-10-20) `/excititor/init`, `/excititor/ingest/run`, `/excititor/ingest/resume`, `/excititor/reconcile` enforce `vex.admin`, normalize provider inputs, and emit deterministic summaries; verified via `dotnet test src/StellaOps.Excititor.WebService.Tests/StellaOps.Excititor.WebService.Tests.csproj --filter FullyQualifiedName~IngestEndpointsTests`.
2. [TODO] EXCITITOR-WEB-01-003 — EXCITITOR-WEB-01-003 Export & verify endpoints
• Prereqs: EXCITITOR-WEB-01-001 (external/completed), EXCITITOR-EXPORT-01-001 (external/completed), EXCITITOR-ATTEST-01-001 (external/completed)
• Current: TODO Add `/excititor/export`, `/excititor/export/{id}`, `/excititor/export/{id}/download`, `/excititor/verify`, returning artifact + attestation metadata with cache awareness.
@@ -297,9 +301,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO Provide export serializer generating canonical OpenVEX documents with optional SBOM references and hash-stable ordering.
- Team: Team Excititor Worker
- Path: `src/StellaOps.Excititor.Worker/TASKS.md`
1. [TODO] EXCITITOR-WORKER-01-002 — EXCITITOR-WORKER-01-002 Resume tokens & retry policy
1. [DONE 2025-10-21] EXCITITOR-WORKER-01-002 — EXCITITOR-WORKER-01-002 Resume tokens & retry policy
• Prereqs: EXCITITOR-WORKER-01-001 (external/completed)
• Current: TODO Implement durable resume markers, exponential backoff with jitter, and quarantine for failing connectors per architecture spec.
• Current: DONE Worker updates connector state with resume tokens + success/failure metadata and applies jittered exponential backoff with quarantine scheduling; unit coverage added for skip/backoff/resume flows.
- **Sprint 7** · Contextual Truth Foundations
- Team: Team Excititor Export
- Path: `src/StellaOps.Excititor.Export/TASKS.md`
@@ -308,12 +312,12 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO Emit consensus+score envelopes in export manifests, include policy/scoring digests, and update offline bundle/ORAS layouts to carry signed VEX responses.
- Team: Team Excititor WebService
- Path: `src/StellaOps.Excititor.WebService/TASKS.md`
1. [TODO] EXCITITOR-WEB-01-004 — Resolve API & signed responses expose `/excititor/resolve`, return signed consensus/score envelopes, document auth.
1. [DONE 2025-10-20] EXCITITOR-WEB-01-004 — Resolve API & signed responses expose `/excititor/resolve`, return signed consensus/score envelopes, document auth.
• Prereqs: —
• Current: TODO
- Team: Team Excititor Worker
- Path: `src/StellaOps.Excititor.Worker/TASKS.md`
1. [TODO] EXCITITOR-WORKER-01-004 — EXCITITOR-WORKER-01-004 TTL refresh & stability damper
1. [DONE 2025-10-21] EXCITITOR-WORKER-01-004 — EXCITITOR-WORKER-01-004 TTL refresh & stability damper
• Prereqs: EXCITITOR-WORKER-01-001 (external/completed), EXCITITOR-CORE-02-001 (external/completed)
• Current: TODO Monitor consensus/VEX TTLs, apply 2448h dampers before flipping published status/score, and trigger re-resolve when base image or kernel fingerprints change.
- **Sprint 8** · Mongo strengthening
@@ -340,9 +344,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: DONE Admin backfill endpoint, CLI command (`stellaops excititor backfill-statements`), integration coverage, and operator runbook published; further automation tracked separately if needed.
- Team: Team Excititor Worker
- Path: `src/StellaOps.Excititor.Worker/TASKS.md`
1. [TODO] EXCITITOR-WORKER-02-001 — EXCITITOR-WORKER-02-001 Resolve Microsoft.Extensions.Caching.Memory advisory
1. [DONE 2025-10-21] EXCITITOR-WORKER-02-001 — EXCITITOR-WORKER-02-001 Resolve Microsoft.Extensions.Caching.Memory advisory
• Prereqs: EXCITITOR-WORKER-01-001 (external/completed)
• Current: TODO Bump `Microsoft.Extensions.Caching.Memory` (and related packages) to the latest .NET 10 preview, regenerate lockfiles, and re-run worker/webservice tests to clear NU1903 high severity warning.
• Current: DONE (2025-10-21) Upgraded Excititor workers/connectors to `Microsoft.Extensions.*` 10.0.0-preview.7.25380.108, restored attestation diagnostics, and re-ran worker + webservice test suites with no NU1903 vulnerabilities.
- **Sprint 8** · Plugin Infrastructure
- Team: Plugin Platform Guild
- Path: `src/StellaOps.Plugin/TASKS.md`
@@ -351,9 +355,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO
- Team: Plugin Platform Guild, Authority Core
- Path: `src/StellaOps.Plugin/TASKS.md`
1. [TODO] PLUGIN-DI-08-002 — Update Authority plugin integration — Flow scoped services through identity-provider registrars, bootstrap flows, and background jobs; add regression coverage around scoped lifetimes. (Coordination session set for 2025-10-20 15:0016:00UTC; document outcomes before implementation.)
1. [DONE] PLUGIN-DI-08-002 — Update Authority plugin integration — Flow scoped services through identity-provider registrars, bootstrap flows, and background jobs; add regression coverage around scoped lifetimes. (Implemented 2025-10-20 with scoped Standard plugin registrations and registry handles.)
• Prereqs: —
• Current: TODO
• Current: DONE (2025-10-20) Standard registrar registers scoped credential/provisioning stores and identity-provider plugins, registry Acquire returns scoped handles, and tests `dotnet test src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard.Tests/StellaOps.Authority.Plugin.Standard.Tests.csproj` + `dotnet test src/StellaOps.Authority/StellaOps.Authority.Tests/StellaOps.Authority.Tests.csproj` validate behaviour.
- **Sprint 9** · Docs & Governance
- Team: Platform Events Guild
- Path: `docs/TASKS.md`
@@ -368,15 +372,15 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 9** · Policy Foundations
- Team: Policy Guild
- Path: `src/StellaOps.Policy/TASKS.md`
1. [TODO] POLICY-CORE-09-004 — Versioned scoring config with schema validation, trust table, and golden fixtures.
1. [DONE] POLICY-CORE-09-004 — Versioned scoring config with schema validation, trust table, and golden fixtures. (2025-10-19)
• Prereqs: —
• Current: TODO
2. [TODO] POLICY-CORE-09-005 — Scoring/quiet engine compute score, enforce VEX-only quiet rules, emit inputs and provenance.
• Current: DONE (2025-10-19)
2. [DONE] POLICY-CORE-09-005 — Scoring/quiet engine compute score, enforce VEX-only quiet rules, emit inputs and provenance. (2025-10-19)
• Prereqs: —
• Current: TODO
3. [TODO] POLICY-CORE-09-006 — Unknown state & confidence decay deterministic bands surfaced in policy outputs.
• Current: DONE (2025-10-19)
3. [DONE] POLICY-CORE-09-006 — Unknown state & confidence decay deterministic bands surfaced in policy outputs. (2025-10-19)
• Prereqs: —
• Current: TODO
• Current: DONE (2025-10-19)
- **Sprint 10** · Backlog
- Team: TBD
- Path: `src/StellaOps.Scanner.Analyzers.Lang.Node/TASKS.md`
@@ -473,7 +477,7 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO
- Team: Authority Core & Security Guild
- Path: `src/StellaOps.Authority/TASKS.md`
1. [DOING] AUTH-DPOP-11-001 — Implement DPoP proof validation + nonce handling for high-value audiences per architecture.
1. [DONE] AUTH-DPOP-11-001 — Implement DPoP proof validation + nonce handling for high-value audiences per architecture. (Redis-configurable nonce store + docs landed 2025-10-20)
• Prereqs: —
• Current: DOING (2025-10-19)
2. [DOING] AUTH-MTLS-11-002 — Add OAuth mTLS client credential support with certificate-bound tokens and introspection updates.
@@ -481,15 +485,15 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: DOING (2025-10-19)
- Team: Signer Guild
- Path: `src/StellaOps.Signer/TASKS.md`
1. [TODO] SIGNER-API-11-101 — `/sign/dsse` pipeline with Authority auth, PoE introspection, release verification, DSSE signing.
1. [DONE] SIGNER-API-11-101 — `/sign/dsse` pipeline with Authority auth, PoE introspection, release verification, DSSE signing.
• Prereqs: —
• Current: TODO
2. [TODO] SIGNER-REF-11-102 — `/verify/referrers` endpoint with OCI lookup, caching, and policy enforcement.
• Current: DONE (2025-10-21) Minimal API host now issues DSSE bundles with PoE validation, release verification, and quota enforcement; integration tests cover success/error paths via `dotnet test src/StellaOps.Signer/StellaOps.Signer.Tests/StellaOps.Signer.Tests.csproj`.
2. [DONE] SIGNER-REF-11-102 — `/verify/referrers` endpoint with OCI lookup, caching, and policy enforcement.
• Prereqs: —
• Current: TODO
3. [TODO] SIGNER-QUOTA-11-103 — Enforce plan quotas, concurrency/QPS limits, artifact size caps with metrics/audit logs.
• Current: DONE (2025-10-21) Added `/api/v1/signer/verify/referrers` returning deterministic JSON responses for trusted/untrusted digests with regression coverage.
3. [DONE] SIGNER-QUOTA-11-103 — Enforce plan quotas, concurrency/QPS limits, artifact size caps with metrics/audit logs.
• Prereqs: —
• Current: TODO
• Current: DONE (2025-10-21) In-memory quota service applies payload caps and per-tenant QPS throttles; tests cover oversize and throttled cases.
- **Sprint 12** · Runtime Guardrails
- Team: Zastava Core Guild
- Path: `src/StellaOps.Zastava.Core/TASKS.md`
@@ -555,25 +559,28 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO
- Team: Scanner WebService Guild
- Path: `src/StellaOps.Scanner.WebService/TASKS.md`
1. [TODO] SCANNER-EVENTS-15-201 — Emit `scanner.report.ready` + `scanner.scan.completed` events.
1. [DONE] SCANNER-EVENTS-15-201 — Emit `scanner.report.ready` + `scanner.scan.completed` events.
• Prereqs: —
• Current: TODO
2. [BLOCKED] SCANNER-EVENTS-16-301 — Redis publisher integration tests once Notify queue adapter ships.
• Prereqs: NOTIFY-QUEUE-15-401 (Wave 1)
• Current: BLOCKED waiting on Notify queue abstraction and Redis adapter deliverables for end-to-end validation.
- **Sprint 16** · Scheduler Intelligence
- Team: Scheduler ImpactIndex Guild
- Path: `src/StellaOps.Scheduler.ImpactIndex/TASKS.md`
1. [DOING] SCHED-IMPACT-16-300 — **STUB** ingest/query using fixtures to unblock Scheduler planning (remove by SP16 end).
1. [DONE (2025-10-20)] SCHED-IMPACT-16-300 — **STUB** ingest/query using fixtures to unblock Scheduler planning (remove by SP16 end).
• Prereqs: SAMPLES-10-001 (external/completed)
• Current: DOING
- Team: Scheduler Models Guild
- Path: `src/StellaOps.Scheduler.Models/TASKS.md`
1. [TODO] SCHED-MODELS-16-103 Versioning/migration helpers (schedule evolution, run state transitions).
1. [DONE (2025-10-20)] SCHED-MODELS-16-103 - Versioning/migration helpers (schedule evolution, run state transitions).
• Prereqs: SCHED-MODELS-16-101 (external/completed)
• Current: TODO
• Current: DONE
- Team: Scheduler Queue Guild
- Path: `src/StellaOps.Scheduler.Queue/TASKS.md`
1. [TODO] SCHED-QUEUE-16-401 Implement queue abstraction + Redis Streams adapter (planner inputs, runner segments) with ack/lease semantics.
1. [DONE (2025-10-20)] SCHED-QUEUE-16-401 - Implement queue abstraction + Redis Streams adapter (planner inputs, runner segments) with ack/lease semantics.
• Prereqs: SCHED-MODELS-16-101 (external/completed)
• Current: TODO
• Current: DONE
- Team: Scheduler Storage Guild
- Path: `src/StellaOps.Scheduler.Storage.Mongo/TASKS.md`
1. [TODO] SCHED-STORAGE-16-201 — Create Mongo collections (schedules, runs, impact_cursors, locks, audit) with indexes/migrations per architecture.
@@ -584,6 +591,18 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
1. [TODO] SCHED-WEB-16-101 — Bootstrap Minimal API host with Authority OpTok + DPoP, health endpoints, plug-in discovery per architecture §§12.
• Prereqs: SCHED-MODELS-16-101 (external/completed)
• Current: TODO
- **Sprint 18** · Launch Readiness
- Team: DevOps Guild
- Path: `ops/devops/TASKS.md`
1. [TODO] DEVOPS-LAUNCH-18-100 - Finalise production environment footprint (clusters, secrets, network overlays) for full-platform go-live.
• Prereqs: —
• Current: TODO
2. [TODO] DEVOPS-LAUNCH-18-900 - Collect "full implementation" sign-off from module owners and consolidate the launch readiness checklist.
• Prereqs: Wave 0 completion
• Current: TODO
3. [TODO] DEVOPS-LAUNCH-18-001 - Production launch cutover rehearsal and runbook publication.
• Prereqs: DEVOPS-LAUNCH-18-100, DEVOPS-LAUNCH-18-900
• Current: TODO
## Wave 1 — 45 task(s) ready after Wave 0
- **Sprint 6** · Excititor Ingest & Formats
@@ -621,9 +640,12 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 9** · DevOps Foundations
- Team: DevOps Guild, Scanner WebService Guild
- Path: `ops/devops/TASKS.md`
1. [TODO] DEVOPS-SCANNER-09-204 — Surface `SCANNER__EVENTS__*` environment variables across docker-compose (dev/stage/airgap) and Helm values, defaulting to share the Redis queue DSN.
1. [DONE] DEVOPS-SCANNER-09-204 — Surface `SCANNER__EVENTS__*` environment variables across docker-compose (dev/stage/airgap) and Helm values, defaulting to share the Redis queue DSN. (2025-10-21)
• Prereqs: SCANNER-EVENTS-15-201 (Wave 0)
• Current: TODO
• Current: DONE (2025-10-21) Compose dev/stage/airgap profiles and Helm values now expose the SCANNER__EVENTS__* toggles; docs (deploy/compose/README.md, docs/ARCHITECTURE_SCANNER.md) call out the new configuration knobs.
2. [DONE] DEVOPS-SCANNER-09-205 — Add Notify smoke stage that tails the Redis stream and asserts `scanner.report.ready`/`scanner.scan.completed` reach Notify WebService in staging. (2025-10-21)
• Prereqs: DEVOPS-SCANNER-09-204 (Wave 0)
• Current: DONE (2025-10-21) `notify-smoke` CI job runs the NotifySmokeCheck tool against staging Redis/Notify using configured secrets; deploy docs enumerate required configuration.
- **Sprint 10** · Backlog
- Team: TBD
- Path: `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`
@@ -649,9 +671,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 10** · Benchmarks
- Team: Bench Guild, Language Analyzer Guild
- Path: `bench/TASKS.md`
1. [TODO] BENCH-SCANNER-10-002 — Wire real language analyzers into bench harness & refresh baselines post-implementation.
1. [DONE] BENCH-SCANNER-10-002 — Wire real language analyzers into bench harness & refresh baselines post-implementation. (2025-10-21)
• Prereqs: SCANNER-ANALYZERS-LANG-10-301 (Wave 0)
• Current: TODO
• Current: DONE (2025-10-21) Harness now invokes language analyzers via `StellaOps.Bench.ScannerAnalyzers`, baseline refreshed against samples/runtime fixtures, and README/config updated for the new flow.
- **Sprint 10** · Scanner Analyzers & SBOM
- Team: Emit Guild
- Path: `src/StellaOps.Scanner.Emit/TASKS.md`
@@ -687,9 +709,15 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 12** · Runtime Guardrails
- Team: Scanner WebService Guild
- Path: `src/StellaOps.Scanner.WebService/TASKS.md`
1. [TODO] SCANNER-RUNTIME-12-301 — Implement `/runtime/events` ingestion endpoint with validation, batching, and storage hooks per Zastava contract.
1. [DONE] SCANNER-RUNTIME-12-301 — Implement `/runtime/events` ingestion endpoint with validation, batching, and storage hooks per Zastava contract. (2025-10-20)
• Prereqs: ZASTAVA-CORE-12-201 (Wave 0)
• Current: TODO
• Current: DONE (2025-10-20) — Mongo persistence + rate limiting shipped; observer fixtures can replay batches end-to-end.
2. [DOING] SCANNER-RUNTIME-12-302 — Implement `/policy/runtime` endpoint joining SBOM baseline + policy verdict, returning admission guidance.
• Prereqs: SCANNER-RUNTIME-12-301 (Wave 1), ZASTAVA-CORE-12-201 (Wave 0)
• Current: DOING (2025-10-20) — Locking response schema with Policy/CLI guilds, wiring determinism tests.
3. [TODO] SCANNER-RUNTIME-12-303 — Align runtime verdicts with canonical policy evaluation (Feedser/Vexer inputs) once upstream dependencies land.
4. [TODO] SCANNER-RUNTIME-12-304 — Surface attestation/Rekor verification results via Authority/Attestor integration.
5. [TODO] SCANNER-RUNTIME-12-305 — Finalize shared fixtures and CI automation with Zastava + CLI teams for runtime APIs.
- Team: Zastava Observer Guild
- Path: `src/StellaOps.Zastava.Observer/TASKS.md`
1. [TODO] ZASTAVA-OBS-12-001 — Build container lifecycle watcher that tails CRI (containerd/cri-o/docker) events and emits deterministic runtime records with buffering + backoff.
@@ -759,12 +787,12 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
• Current: TODO
- Team: Scheduler Queue Guild
- Path: `src/StellaOps.Scheduler.Queue/TASKS.md`
1. [TODO] SCHED-QUEUE-16-402 Add NATS JetStream adapter with configuration binding, health probes, failover.
1. [DONE (2025-10-20)] SCHED-QUEUE-16-402 - Add NATS JetStream adapter with configuration binding, health probes, failover.
• Prereqs: SCHED-QUEUE-16-401 (Wave 0)
• Current: TODO
2. [TODO] SCHED-QUEUE-16-403 Dead-letter handling + metrics (queue depth, retry counts), configuration toggles.
• Current: DONE
2. [DONE (2025-10-20)] SCHED-QUEUE-16-403 - Dead-letter handling + metrics (queue depth, retry counts), configuration toggles.
• Prereqs: SCHED-QUEUE-16-401 (Wave 0)
• Current: TODO
• Current: DONE
- Team: Scheduler Storage Guild
- Path: `src/StellaOps.Scheduler.Storage.Mongo/TASKS.md`
1. [TODO] SCHED-STORAGE-16-203 — Audit/logging pipeline + run stats materialized views for UI.
@@ -983,9 +1011,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 13** · UX & CLI Experience
- Team: DevEx/CLI
- Path: `src/StellaOps.Cli/TASKS.md`
1. [TODO] CLI-OFFLINE-13-006 — CLI-OFFLINE-13-006 Offline kit workflows
1. [DONE] CLI-OFFLINE-13-006 — CLI-OFFLINE-13-006 Offline kit workflows
• Prereqs: DEVOPS-OFFLINE-14-002 (Wave 2)
• Current: TODO Implement `offline kit pull/import/status` commands with integrity checks, resumable downloads, and doc updates.
• Current: DONE (2025-10-21) Delivered `offline kit pull/import/status` commands with resumable downloads, digest/metadata validation, CLI metrics + docs, and regression coverage (`dotnet test src/StellaOps.Cli.Tests`).
- Team: DevEx/CLI, Scanner WebService Guild
- Path: `src/StellaOps.Cli/TASKS.md`
1. [TODO] CLI-RUNTIME-13-008 — CLI-RUNTIME-13-008 Runtime policy contract sync
@@ -1121,17 +1149,20 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 15** · Notify Foundations
- Team: Notify Connectors Guild
- Path: `src/StellaOps.Notify.Connectors.Email/TASKS.md`
1. [DOING] NOTIFY-CONN-EMAIL-15-702 — Add DKIM signing optional support and health/test-send flows.
1. [BLOCKED] NOTIFY-CONN-EMAIL-15-702 — Add DKIM signing optional support and health/test-send flows.
• Prereqs: NOTIFY-CONN-EMAIL-15-701 (Wave 4)
• Current: TODO
• Current: BLOCKED waiting on base SMTP connector implementation (NOTIFY-CONN-EMAIL-15-701).
- Path: `src/StellaOps.Notify.Connectors.Slack/TASKS.md`
1. [DOING] NOTIFY-CONN-SLACK-15-502 — Health check & test-send support with minimal scopes and redacted tokens.
1. [DONE] NOTIFY-CONN-SLACK-15-502 — Health check & test-send support with minimal scopes and redacted tokens.
• Prereqs: NOTIFY-CONN-SLACK-15-501 (Wave 4)
• Current: TODO
- Path: `src/StellaOps.Notify.Connectors.Teams/TASKS.md`
1. [DOING] NOTIFY-CONN-TEAMS-15-602 — Provide health/test-send support with fallback text for legacy clients.
1. [DONE] NOTIFY-CONN-TEAMS-15-602 — Provide health/test-send support with fallback text for legacy clients.
• Prereqs: NOTIFY-CONN-TEAMS-15-601 (Wave 4)
• Current: TODO
2. [DONE] NOTIFY-CONN-TEAMS-15-604 — Align Teams health endpoint output with preview metadata redaction.
• Prereqs: NOTIFY-CONN-TEAMS-15-602 (Wave 5)
• Current: DONE
- Path: `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`
1. [DOING] NOTIFY-CONN-WEBHOOK-15-802 — Health/test-send support with signature validation hints and secret management.
• Prereqs: NOTIFY-CONN-WEBHOOK-15-801 (Wave 4)
@@ -1165,19 +1196,19 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 15** · Notify Foundations
- Team: Notify Connectors Guild
- Path: `src/StellaOps.Notify.Connectors.Email/TASKS.md`
1. [TODO] NOTIFY-CONN-EMAIL-15-703 — Package Email connector as restart-time plug-in (manifest + host registration).
1. [DONE] NOTIFY-CONN-EMAIL-15-703 — Package Email connector as restart-time plug-in (manifest + host registration).
• Prereqs: NOTIFY-CONN-EMAIL-15-702 (Wave 5)
• Current: TODO
- Path: `src/StellaOps.Notify.Connectors.Slack/TASKS.md`
1. [TODO] NOTIFY-CONN-SLACK-15-503 — Package Slack connector as restart-time plug-in (manifest + host registration).
1. [DONE] NOTIFY-CONN-SLACK-15-503 — Package Slack connector as restart-time plug-in (manifest + host registration).
• Prereqs: NOTIFY-CONN-SLACK-15-502 (Wave 5)
• Current: TODO
- Path: `src/StellaOps.Notify.Connectors.Teams/TASKS.md`
1. [TODO] NOTIFY-CONN-TEAMS-15-603 — Package Teams connector as restart-time plug-in (manifest + host registration).
1. [DONE] NOTIFY-CONN-TEAMS-15-603 — Package Teams connector as restart-time plug-in (manifest + host registration).
• Prereqs: NOTIFY-CONN-TEAMS-15-602 (Wave 5)
• Current: TODO
- Path: `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`
1. [TODO] NOTIFY-CONN-WEBHOOK-15-803 — Package Webhook connector as restart-time plug-in (manifest + host registration).
1. [DONE] NOTIFY-CONN-WEBHOOK-15-803 — Package Webhook connector as restart-time plug-in (manifest + host registration).
• Prereqs: NOTIFY-CONN-WEBHOOK-15-802 (Wave 5)
• Current: TODO
@@ -1193,9 +1224,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 7** · Contextual Truth Foundations
- Team: Team Core Engine & Data Science
- Path: `src/StellaOps.Concelier.Core/TASKS.md`
1. [TODO] FEEDCORE-ENGINE-07-002 — FEEDCORE-ENGINE-07-002 Noise prior computation service
1. [DONE] FEEDCORE-ENGINE-07-002 — FEEDCORE-ENGINE-07-002 Noise prior computation service
• Prereqs: FEEDCORE-ENGINE-07-001 (Wave 7)
• Current: TODO Build rule-based learner capturing false-positive priors per package/env, persist summaries, and expose APIs for Excititor/scan suppressors with reproducible statistics.
• Current: DONE (2025-10-21) Added NoisePriorService with rule-based aggregation of advisory statements, repository contracts for deterministic summaries, DI helper, and unit tests covering heuristics and persistence.
## Wave 9 — 1 task(s) ready after Wave 8
- **Sprint 7** · Contextual Truth Foundations
@@ -1249,22 +1280,22 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 8** · Mirror Distribution
- Team: BE-Conn-Stella
- Path: `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`
1. [DOING] FEEDCONN-STELLA-08-001 — Implement Concelier mirror fetcher hitting `https://<domain>.stella-ops.org/concelier/exports/index.json`, verify signatures/digests, and persist raw documents with provenance.
1. [DONE] FEEDCONN-STELLA-08-001 — Implement Concelier mirror fetcher hitting `https://<domain>.stella-ops.org/concelier/exports/index.json`, verify signatures/digests, and persist raw documents with provenance.
• Prereqs: CONCELIER-EXPORT-08-201 (Wave 12)
• Current: DOING (2025-10-19) Client consuming new signed mirror bundles/index, standing up verification + storage plumbing ahead of DTO mapping.
• Current: DONE (2025-10-20) Fetch job persists manifest/bundle metadata, enforces digest and detached JWS verification (fallback PEM support), and regression coverage captured via `dotnet test src/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj`.
## Wave 16 — 1 task(s) ready after Wave 15
- **Sprint 8** · Mirror Distribution
- Team: BE-Conn-Stella
- Path: `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`
1. [TODO] FEEDCONN-STELLA-08-002 — Map mirror payloads into canonical advisory DTOs with provenance referencing mirror domain + original source metadata.
1. [DONE] FEEDCONN-STELLA-08-002 — Map mirror payloads into canonical advisory DTOs with provenance referencing mirror domain + original source metadata. (2025-10-20)
• Prereqs: FEEDCONN-STELLA-08-001 (Wave 15)
• Current: TODO
• Current: DONE (2025-10-20) `MirrorAdvisoryMapper` emits canonical advisories and fixtures assert parity with exporter outputs.
## Wave 17 — 1 task(s) ready after Wave 16
- **Sprint 8** · Mirror Distribution
- Team: BE-Conn-Stella
- Path: `src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md`
1. [TODO] FEEDCONN-STELLA-08-003 — Add incremental cursor + resume support (per-export fingerprint) and document configuration for downstream Concelier instances.
1. [DONE] FEEDCONN-STELLA-08-003 — Add incremental cursor + resume support (per-export fingerprint) and document configuration for downstream Concelier instances. (2025-10-20)
• Prereqs: FEEDCONN-STELLA-08-002 (Wave 16)
• Current: TODO
• Current: DONE (2025-10-20) Connector records per-export fingerprints, resumes pending documents, and ops guide documents offline configuration knobs.

View File

@@ -2,16 +2,16 @@
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="local" value="local-nuget" />
<add key="mirror" value="https://mirrors.ablera.dev/nuget/nuget-mirror/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="local">
<package pattern="Mongo2Go" />
<package pattern="Microsoft.Extensions.Http.Polly" />
</packageSource>
<packageSource key="mirror">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>

View File

@@ -2,211 +2,33 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
| Sprint | Theme | Tasks File Path | Status | Type of Specialist | Task ID | Task Description |
| --- | --- | --- | --- | --- | --- | --- |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-001 | SemVer primitive range-style metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md. This task lays the groundwork—complete the SemVer helper updates before teammates pick up FEEDMODELS-SCHEMA-01-002/003 and FEEDMODELS-SCHEMA-02-900. Use ./src/FASTER_MODELING_AND_NORMALIZATION.md for the target rule structure. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-002 | Provenance decision rationale field<br>Instructions to work:<br>AdvisoryProvenance now carries `decisionReason` and docs/tests were updated. Connectors and merge tasks should populate the field when applying precedence/freshness/tie-breaker logic; see src/StellaOps.Concelier.Models/PROVENANCE_GUIDELINES.md for usage guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-003 | Normalized version rules collection<br>Instructions to work:<br>`AffectedPackage.NormalizedVersions` and supporting comparer/docs/tests shipped. Connector owners must emit rule arrays per ./src/FASTER_MODELING_AND_NORMALIZATION.md and report progress via FEEDMERGE-COORD-02-900 so merge/storage backfills can proceed. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-02-900 | Range primitives for SemVer/EVR/NEVRA metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md before resuming this stalled effort. Confirm helpers align with the new `NormalizedVersions` representation so connectors finishing in Sprint 2 can emit consistent metadata. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>Shared `SemVerRangeRuleBuilder` now outputs primitives + normalized rules per `FASTER_MODELING_AND_NORMALIZATION.md`; CVE/GHSA connectors consuming the API have verified fixtures. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>AdvisoryStore dual-writes flattened `normalizedVersions` when `concelier.storage.enableSemVerStyle` is set; migration `20251011-semver-style-backfill` updates historical records and docs outline the rollout. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence<br>Storage now persists `provenance.decisionReason` for advisories and merge events; tests cover round-trips. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Bootstrapper seeds compound/sparse indexes for flattened normalized rules and `docs/dev/mongo_indices.md` documents query guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Updated constructors/tests keep storage suites passing with the new feature flag defaults. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Install/runbooks document connected vs air-gapped resilience profiles and monitoring hooks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Operator guides now call out `route/status/subject/clientId/scopes/bypass/remote` audit fields and SIEM triggers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and links audit signals to the rollout checklist. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.HOST | Rate limiter policy binding<br>Authority host now applies configuration-driven fixed windows to `/token`, `/authorize`, and `/internal/*`; integration tests assert 429 + `Retry-After` headers; docs/config samples refreshed for Docs guild diagrams. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.BUILD | Authority rate-limiter follow-through<br>`Security.RateLimiting` now fronts token/authorize/internal limiters; Authority + Configuration matrices (`dotnet test src/StellaOps.Authority/StellaOps.Authority.sln`, `dotnet test src/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj`) passed on 2025-10-11; awaiting #authority-core broadcast. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHCORE-BUILD-OPENIDDICT / AUTHCORE-STORAGE-DEVICE-TOKENS / AUTHCORE-BOOTSTRAP-INVITES | Address remaining Authority compile blockers (OpenIddict transaction shim, token device document, bootstrap invite cleanup) so `dotnet build src/StellaOps.Authority.sln` returns success. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | PLG6.DOC | Plugin developer guide polish<br>Section 9 now documents rate limiter metadata, config keys, and lockout interplay; YAML samples updated alongside Authority config templates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-001 | Fetch pipeline & state tracking<br>Summary planner now drives monthly/yearly VINCE fetches, persists pending summaries/notes, and hydrates VINCE detail queue with telemetry.<br>Team instructions: Read ./AGENTS.md and src/StellaOps.Concelier.Connector.CertCc/AGENTS.md. Coordinate daily with Models/Merge leads so new normalizedVersions output and provenance tags stay aligned with ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-002 | VINCE note detail fetcher<br>Summary planner queues VINCE note detail endpoints, persists raw JSON with SHA/ETag metadata, and records retry/backoff metrics. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-003 | DTO & parser implementation<br>Added VINCE DTO aggregate, Markdown→text sanitizer, vendor/status/vulnerability parsers, and parser regression fixture. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-004 | Canonical mapping & range primitives<br>VINCE DTO aggregate flows through `CertCcMapper`, emitting vendor range primitives + normalized version rules that persist via `_advisoryStore`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-005 | Deterministic fixtures/tests<br>Snapshot harness refreshed 2025-10-12; `certcc-*.snapshot.json` regenerated and regression suite green without UPDATE flag drift. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-006 | Telemetry & documentation<br>`CertCcDiagnostics` publishes summary/detail/parse/map metrics (meter `StellaOps.Concelier.Connector.CertCc`), README documents instruments, and log guidance captured for Ops on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-007 | Connector test harness remediation<br>Harness now wires `AddSourceCommon`, resets `FakeTimeProvider`, and passes canned-response regression run dated 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-008 | Snapshot coverage handoff<br>Fixtures regenerated with normalized ranges + provenance fields on 2025-10-11; QA handoff notes published and merge backfill unblocked. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-012 | Schema sync & snapshot regen follow-up<br>Fixtures regenerated with normalizedVersions + provenance decision reasons; handoff notes updated for Merge backfill 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-009 | Detail/map reintegration plan<br>Staged reintegration plan published in `src/StellaOps.Concelier.Connector.CertCc/FEEDCONN-CERTCC-02-009_PLAN.md`; coordinates enablement with FEEDCONN-CERTCC-02-004. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-010 | Partial-detail graceful degradation<br>Detail fetch now tolerates 404/403/410 responses and regression tests cover mixed endpoint availability. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Distro.RedHat/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-REDHAT-02-001 | Fixture validation sweep<br>Instructions to work:<br>Fixtures regenerated post-model-helper rollout; provenance ordering and normalizedVersions scaffolding verified via tests. Conflict resolver deltas logged in src/StellaOps.Concelier.Connector.Distro.RedHat/CONFLICT_RESOLVER_NOTES.md for Sprint 3 consumers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-001 | Canonical mapping & range primitives<br>Mapper emits SemVer rules (`scheme=apple:*`); fixtures regenerated with trimmed references + new RSR coverage, update tooling finalized. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-002 | Deterministic fixtures/tests<br>Sanitized live fixtures + regression snapshots wired into tests; normalized rule coverage asserted. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-003 | Telemetry & documentation<br>Apple meter metrics wired into Concelier WebService OpenTelemetry configuration; README and fixtures document normalizedVersions coverage. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-004 | Live HTML regression sweep<br>Sanitised HT125326/HT125328/HT106355/HT214108/HT215500 fixtures recorded and regression tests green on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-005 | Fixture regeneration tooling<br>`UPDATE_APPLE_FIXTURES=1` flow fetches & rewrites fixtures; README documents usage.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Connector.Vndr.Apple/AGENTS.md. Resume stalled tasks, ensuring normalizedVersions output and fixtures align with ./src/FASTER_MODELING_AND_NORMALIZATION.md before handing data to the conflict sprint. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance<br>Team instructions: Read ./AGENTS.md and each module's AGENTS file. Adopt the `NormalizedVersions` array emitted by the models sprint, wiring provenance `decisionReason` where merge overrides occur. Follow ./src/FASTER_MODELING_AND_NORMALIZATION.md; report via src/StellaOps.Concelier.Merge/TASKS.md (FEEDMERGE-COORD-02-900). Progress 2025-10-11: GHSA/OSV emit normalized arrays with refreshed fixtures; CVE mapper now surfaces SemVer normalized ranges; NVD/KEV adoption pending; outstanding follow-ups include FEEDSTORAGE-DATA-02-001, FEEDMERGE-ENGINE-02-002, and rolling `tools/FixtureUpdater` updates across connectors. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-CVE-02-003 | CVE normalized versions uplift |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-KEV-02-003 | KEV normalized versions propagation |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-04-003 | OSV parity fixture refresh |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-10) | Team WebService & Authority | FEEDWEB-DOCS-01-001 | Document authority toggle & scope requirements<br>Quickstart carries toggle/scope guidance pending docs guild review (no change this sprint). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Operator docs now outline connected vs air-gapped resilience profiles and monitoring cues. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Audit logging guidance highlights `route/status/subject/clientId/scopes/bypass/remote` fields and SIEM alerts. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and ties audit signals to rollout checks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-19) | Team WebService & Authority | FEEDWEB-OPS-01-006 | Rename plugin drop directory to namespaced path<br>Build outputs now point at `StellaOps.Concelier.PluginBinaries`/`StellaOps.Authority.PluginBinaries`; defaults/docs/tests updated to reflect the new layout. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | FEEDWEB-OPS-01-007 | Authority resilience adoption<br>Deployment docs and CLI notes explain the LIB5 resilience knobs for rollout.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.WebService/AGENTS.md. These items were mid-flight; resume implementation ensuring docs/operators receive timely updates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCORE-ENGINE-01-001 | CORE8.RL — Rate limiter plumbing validated; integration tests green and docs handoff recorded for middleware ordering + Retry-After headers (see `docs/dev/authority-rate-limit-tuning-outline.md` for continuing guidance). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCRYPTO-ENGINE-01-001 | SEC3.A — Shared metadata resolver confirmed via host test run; SEC3.B now unblocked for tuning guidance (outline captured in `docs/dev/authority-rate-limit-tuning-outline.md`). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-13) | Team Authority Platform & Security Guild | AUTHSEC-DOCS-01-002 | SEC3.B — Published `docs/security/rate-limits.md` with tuning matrix, alert thresholds, and lockout interplay guidance; Docs guild can lift copy into plugin guide. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHSEC-CRYPTO-02-001 | SEC5.B1 — Introduce libsodium signing provider and parity tests to unblock CLI verification enhancements. |
| Sprint 1 | Bootstrap & Replay Hardening | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Security Guild | AUTHSEC-CRYPTO-02-004 | SEC5.D/E — Finish bootstrap invite lifecycle (API/store/cleanup) and token device heuristics; build currently red due to pending handler integration. |
| Sprint 1 | Developer Tooling | src/StellaOps.Cli/TASKS.md | DONE (2025-10-15) | DevEx/CLI | AUTHCLI-DIAG-01-001 | Surface password policy diagnostics in CLI startup/output so operators see weakened overrides immediately.<br>CLI now loads Authority plug-ins at startup, logs weakened password policies (length/complexity), and regression coverage lives in `StellaOps.Cli.Tests/Services/AuthorityDiagnosticsReporterTests`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHPLUG-DOCS-01-001 | PLG6.DOC — Developer guide copy + diagrams merged 2025-10-11; limiter guidance incorporated and handed to Docs guild for asset export. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-12) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>`SemVerRangeRuleBuilder` shipped 2025-10-12 with comparator/`||` support and fixtures aligning to `FASTER_MODELING_AND_NORMALIZATION.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Indexes seeded + docs updated 2025-10-11 to cover flattened normalized rules for connector adoption. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDMERGE-ENGINE-02-002 | Normalized versions union & dedupe<br>Affected package resolver unions/dedupes normalized rules, stamps merge provenance with `decisionReason`, and tests cover the rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-004 | GHSA credits & ecosystem severity mapping |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-005 | GitHub quota monitoring & retries |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-006 | Production credential & scheduler rollout |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-007 | Credit parity regression fixtures |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-004 | NVD CVSS & CWE precedence payloads |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-005 | NVD merge/export parity regression |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-004 | OSV references & credits alignment |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-005 | Fixture updater workflow<br>Resolved 2025-10-12: OSV mapper now derives canonical PURLs for Go + scoped npm packages when raw payloads omit `purl`; conflict fixtures unchanged for invalid npm names. Verified via `dotnet test src/StellaOps.Concelier.Connector.Osv.Tests`, `src/StellaOps.Concelier.Connector.Ghsa.Tests`, `src/StellaOps.Concelier.Connector.Nvd.Tests`, and backbone normalization/storage suites. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Acsc/TASKS.md | DONE (2025-10-12) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ACSC-02-001 … 02-008 | Fetch→parse→map pipeline, fixtures, diagnostics, and README finished 2025-10-12; downstream export parity captured via FEEDEXPORT-JSON-04-001 / FEEDEXPORT-TRIVY-04-001 (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cccs/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CCCS-02-001 … 02-008 | Observability meter, historical harvest plan, and DOM sanitizer refinements wrapped; ops notes live under `docs/ops/concelier-cccs-operations.md` with fixtures validating EN/FR list handling. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.CertBund/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CERTBUND-02-001 … 02-008 | Telemetry/docs (02-006) and history/locale sweep (02-007) completed alongside pipeline; runbook `docs/ops/concelier-certbund-operations.md` captures locale guidance and offline packaging. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kisa/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-KISA-02-001 … 02-007 | Connector, tests, and telemetry/docs (02-006) finalized; localisation notes in `docs/dev/kisa_connector_notes.md` complete rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Bdu/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-RUBDU-02-001 … 02-008 | Fetch/parser/mapper refinements, regression fixtures, telemetry/docs, access options, and trusted root packaging all landed; README documents offline access strategy. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Nkcki/TASKS.md | DONE (2025-10-13) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-NKCKI-02-001 … 02-008 | Listing fetch, parser, mapper, fixtures, telemetry/docs, and archive plan finished; Mongo2Go/libcrypto dependency resolved via bundled OpenSSL noted in ops guide. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ICSCISA-02-001 … 02-011 | Feed parser attachment fixes, SemVer exact values, regression suites, telemetry/docs updates, and handover complete; ops runbook now details attachment verification + proxy usage. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CISCO-02-001 … 02-007 | OAuth fetch pipeline, DTO/mapping, tests, and telemetry/docs shipped; monitoring/export integration follow-ups recorded in Ops docs and exporter backlog (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Msrc/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-MSRC-02-001 … 02-008 | Azure AD onboarding (02-008) unblocked fetch/parse/map pipeline; fixtures, telemetry/docs, and Offline Kit guidance published in `docs/ops/concelier-msrc-operations.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-15) | Team Connector Support & Monitoring | FEEDCONN-CVE-02-001 … 02-002 | CVE data-source selection, fetch pipeline, and docs landed 2025-10-10. 2025-10-15: smoke verified using the seeded mirror fallback; connector now logs a warning and pulls from `seed-data/cve/` until live CVE Services credentials arrive. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Support & Monitoring | FEEDCONN-KEV-02-001 … 02-002 | KEV catalog ingestion, fixtures, telemetry, and schema validation completed 2025-10-12; ops dashboard published. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-01-001 | Canonical schema docs refresh<br>Updated canonical schema + provenance guides with SemVer style, normalized version rules, decision reason change log, and migration notes. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-001 | Concelier-SemVer Playbook<br>Published merge playbook covering mapper patterns, dedupe flow, indexes, and rollout checklist. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-002 | Normalized versions query guide<br>Delivered Mongo index/query addendum with `$unwind` recipes, dedupe checks, and operational checklist.<br>Instructions to work:<br>DONE Read ./AGENTS.md and docs/AGENTS.md. Document every schema/index/query change produced in Sprint 1-2 leveraging ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-001 | Canonical merger implementation<br>`CanonicalMerger` ships with freshness/tie-breaker logic, provenance, and unit coverage feeding Merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-002 | Field precedence and tie-breaker map<br>Field precedence tables and tie-breaker metrics wired into the canonical merge flow; docs/tests updated.<br>Instructions to work:<br>Read ./AGENTS.md and core AGENTS. Implement the conflict resolver exactly as specified in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md, coordinating with Merge and Storage teammates. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-03-001 | Merge event provenance audit prep<br>Merge events now persist `fieldDecisions` and analytics-ready provenance snapshots. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>Dual-write/backfill flag delivered; migration + options validated in tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Storage tests adjusted for normalized versions/decision reasons.<br>Instructions to work:<br>Read ./AGENTS.md and storage AGENTS. Extend merge events with decision reasons and analytics views to support the conflict rules, and deliver the dual-write/backfill for `NormalizedVersions` + `decisionReason` so connectors can roll out safely. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-001 | GHSA/NVD/OSV conflict rules<br>Merge pipeline consumes `CanonicalMerger` output prior to precedence merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-002 | Override metrics instrumentation<br>Merge events capture per-field decisions; counters/logs align with conflict rules. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-003 | Reference & credit union pipeline<br>Canonical merge preserves unions with updated tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-QA-04-001 | End-to-end conflict regression suite<br>Added regression tests (`AdvisoryMergeServiceTests`) covering canonical + precedence flow.<br>Instructions to work:<br>Read ./AGENTS.md and merge AGENTS. Integrate the canonical merger, instrument metrics, and deliver comprehensive regression tests following ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-002 | GHSA conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-NVD-04-002 | NVD conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-OSV-04-002 | OSV conflict regression fixtures<br>Instructions to work:<br>Read ./AGENTS.md and module AGENTS. Produce fixture triples supporting the precedence/tie-breaker paths defined in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md and hand them to Merge QA. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-11) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-001 | Concelier Conflict Rules<br>Runbook published at `docs/ops/concelier-conflict-resolution.md`; metrics/log guidance aligned with Sprint 3 merge counters. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-16) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-002 | Conflict runbook ops rollout<br>Ops review completed, alert thresholds applied, and change log appended in `docs/ops/concelier-conflict-resolution.md`; task closed after connector signals verified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-15) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-04-001 | Advisory schema parity (description/CWE/canonical metric)<br>Extend `Advisory` and related records with description text, CWE collection, and canonical metric pointer; refresh validation + serializer determinism tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-003 | Canonical merger parity for new fields<br>Teach `CanonicalMerger` to populate description, CWEResults, and canonical metric pointer with provenance + regression coverage. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-004 | Reference normalization & freshness instrumentation cleanup<br>Implement URL normalization for reference dedupe, align freshness-sensitive instrumentation, and add analytics tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-004 | Merge pipeline parity for new advisory fields<br>Ensure merge service + merge events surface description/CWE/canonical metric decisions with updated metrics/tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-005 | Connector coordination for new advisory fields<br>GHSA/NVD/OSV connectors now ship description, CWE, and canonical metric data with refreshed fixtures; merge coordination log updated and exporters notified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.Json/TASKS.md | DONE (2025-10-15) | Team Exporters JSON | FEEDEXPORT-JSON-04-001 | Surface new advisory fields in JSON exporter<br>Update schemas/offline bundle + fixtures once model/core parity lands.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.Json.Tests` validated canonical metric/CWE emission. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-15) | Team Exporters Trivy DB | FEEDEXPORT-TRIVY-04-001 | Propagate new advisory fields into Trivy DB package<br>Extend Bolt builder, metadata, and regression tests for the expanded schema.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.TrivyDb.Tests` confirmed canonical metric/CWE propagation. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-16) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-004 | Harden CVSS fallback so canonical metric ids persist when GitHub omits vectors; extend fixtures and document severity precedence hand-off to Merge. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-16) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-04-005 | Map OSV advisories lacking CVSS vectors to canonical metric ids/notes and document CWE provenance quirks; schedule parity fixture updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-001 | Stand up canonical VEX claim/consensus records with deterministic serializers so Storage/Exports share a stable contract. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-002 | Implement trust-weighted consensus resolver with baseline policy weights, justification gates, telemetry output, and majority/tie handling. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-003 | Publish shared connector/exporter/attestation abstractions and deterministic query signature utilities for cache/attestation workflows. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-001 | Established policy options & snapshot provider covering baseline weights/overrides. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-002 | Policy evaluator now feeds consensus resolver with immutable snapshots. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-003 | Author policy diagnostics, CLI/WebService surfacing, and documentation updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-004 | Implement YAML/JSON schema validation and deterministic diagnostics for operator bundles. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-005 | Add policy change tracking, snapshot digests, and telemetry/logging hooks. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-15) | Team Excititor Storage | EXCITITOR-STORAGE-01-001 | Mongo mapping registry plus raw/export entities and DI extensions in place. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-16) | Team Excititor Storage | EXCITITOR-STORAGE-01-004 | Build provider/consensus/cache class maps and related collections. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-15) | Team Excititor Export | EXCITITOR-EXPORT-01-001 | Export engine delivers cache lookup, manifest creation, and policy integration. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-17) | Team Excititor Export | EXCITITOR-EXPORT-01-004 | Connect export engine to attestation client and persist Rekor metadata. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-001 | Implement in-toto predicate + DSSE builder providing envelopes for export attestation. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Connectors.Abstractions/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors | EXCITITOR-CONN-ABS-01-001 | Deliver shared connector context/base classes so provider plug-ins can be activated via WebService/Worker. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-17) | Team Excititor WebService | EXCITITOR-WEB-01-001 | Scaffold minimal API host, DI, and `/excititor/status` endpoint integrating policy, storage, export, and attestation services. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Worker/TASKS.md | DONE (2025-10-17) | Team Excititor Worker | EXCITITOR-WORKER-01-001 | Create Worker host with provider scheduling and logging to drive recurring pulls/reconciliation. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CSAF-01-001 | Implement CSAF normalizer foundation translating provider documents into `VexClaim` entries. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CYCLONE-01-001 | Implement CycloneDX VEX normalizer capturing `analysis` state and component references. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-OPENVEX-01-001 | Implement OpenVEX normalizer to ingest attestations into canonical claims with provenance. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-001 | Ship Red Hat CSAF provider metadata discovery enabling incremental pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-002 | Fetch CSAF windows with ETag handling, resume tokens, quarantine on schema errors, and persist raw docs. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-003 | Populate provider trust overrides (cosign issuer, identity regex) and provenance hints for policy evaluation/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-004 | Persist resume cursors (last updated timestamp/document hashes) in storage and reload during fetch to avoid duplicates. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-005 | Register connector in Worker/WebService DI, add scheduled jobs, and document CLI triggers for Red Hat CSAF pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-006 | Add CSAF normalization parity fixtures ensuring RHSA-specific metadata is preserved. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-001 | Implement Cisco CSAF endpoint discovery/auth to unlock paginated pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-002 | Implement Cisco CSAF paginated fetch loop with dedupe and raw persistence support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.SUSE.RancherVEXHub/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors SUSE | EXCITITOR-CONN-SUSE-01-001 | Build Rancher VEX Hub discovery/subscription path with offline snapshot support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors MSRC | EXCITITOR-CONN-MS-01-001 | Deliver AAD onboarding/token cache for MSRC CSAF ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Oracle.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Oracle | EXCITITOR-CONN-ORACLE-01-001 | Implement Oracle CSAF catalogue discovery with CPU calendar awareness. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Ubuntu.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Ubuntu | EXCITITOR-CONN-UBUNTU-01-001 | Implement Ubuntu CSAF discovery and channel selection for USN ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-001 | Wire OCI discovery/auth to fetch OpenVEX attestations for configured images. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-002 | Attestation fetch & verify loop download DSSE attestations, trigger verification, handle retries/backoff, persist raw statements. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-003 | Provenance metadata & policy hooks emit image, subject digest, issuer, and trust metadata for policy weighting/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Cli/TASKS.md | DONE (2025-10-18) | DevEx/CLI | EXCITITOR-CLI-01-001 | Add `excititor` CLI verbs bridging to WebService with consistent auth and offline UX. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-19) | Team Excititor Core & Policy | EXCITITOR-CORE-02-001 | Context signal schema prep extend consensus models with severity/KEV/EPSS fields and update canonical serializers. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-19) | Team Excititor Policy | EXCITITOR-POLICY-02-001 | Scoring coefficients & weight ceilings add α/β options, weight boosts, and validation guidance. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-02-001 | Statement events & scoring signals immutable VEX statements store, consensus signal fields, and migration `20251019-consensus-signals-statements` with tests (`dotnet test src/StellaOps.Excititor.Core.Tests/StellaOps.Excititor.Core.Tests.csproj`, `dotnet test src/StellaOps.Excititor.Storage.Mongo.Tests/StellaOps.Excititor.Storage.Mongo.Tests.csproj`). |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.WebService/TASKS.md | TODO | Team Excititor WebService | EXCITITOR-WEB-01-004 | Resolve API & signed responses expose `/excititor/resolve`, return signed consensus/score envelopes, document auth. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-20) | Team Excititor WebService | EXCITITOR-WEB-01-002 | Ingest & reconcile endpoints scope-enforced `/excititor/init`, `/excititor/ingest/run`, `/excititor/ingest/resume`, `/excititor/reconcile`; regression via `dotnet test … --filter FullyQualifiedName~IngestEndpointsTests`. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-20) | Team Excititor WebService | EXCITITOR-WEB-01-004 | Resolve API & signed responses expose `/excititor/resolve`, return signed consensus/score envelopes, document auth. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.WebService/TASKS.md | TODO | Team Excititor WebService | EXCITITOR-WEB-01-005 | Mirror distribution endpoints expose download APIs for downstream Excititor instances. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-002 | Rekor v2 client integration ship transparency log client with retries and offline queue. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Worker/TASKS.md | TODO | Team Excititor Worker | EXCITITOR-WORKER-01-004 | TTL refresh & stability damper schedule re-resolve loops and guard against status flapping. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Worker/TASKS.md | DONE (2025-10-21) | Team Excititor Worker | EXCITITOR-WORKER-01-004 | TTL refresh & stability damper schedule re-resolve loops and guard against status flapping. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Export/TASKS.md | TODO | Team Excititor Export | EXCITITOR-EXPORT-01-005 | Score & resolve envelope surfaces include signed consensus/score artifacts in exports. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Export/TASKS.md | TODO | Team Excititor Export | EXCITITOR-EXPORT-01-006 | Quiet provenance packaging attach quieted-by statement IDs, signers, justification codes to exports and attestations. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Export/TASKS.md | TODO | Team Excititor Export | EXCITITOR-EXPORT-01-007 | Mirror bundle + domain manifest publish signed consensus bundles for mirrors. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md | TODO | Excititor Connectors Stella | EXCITITOR-CONN-STELLA-07-001 | Excititor mirror connector ingest signed mirror bundles and map to VexClaims with resume handling. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-19) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-07-001 | Advisory event log & asOf queries surface immutable statements and replay capability. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-19) | Concelier WebService Guild | FEEDWEB-EVENTS-07-001 | Advisory event replay API expose `/concelier/advisories/{key}/replay` with `asOf` filter, hex hashes, and conflict data. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | TODO | Team Core Engine & Data Science | FEEDCORE-ENGINE-07-002 | Noise prior computation service learn false-positive priors and expose deterministic summaries. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-21) | Team Core Engine & Data Science | FEEDCORE-ENGINE-07-002 | Noise prior computation service learn false-positive priors and expose deterministic summaries. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | TODO | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-07-003 | Unknown state ledger & confidence seeding persist unknown flags, seed confidence bands, expose query surface. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | TODO | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-07-001 | Advisory statement & conflict collections provision Mongo schema/indexes for event-sourced merge. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-20) | BE-Merge | FEEDMERGE-ENGINE-07-001 | Conflict sets & explainers persist conflict materialization and replay hashes for merge decisions. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Normalization & Storage Backbone | FEEDSTORAGE-MONGO-08-001 | Causal-consistent Concelier storage sessions<br>Scoped session facilitator registered, repositories accept optional session handles, and replica-set failover tests verify read-your-write + monotonic reads. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Authority/TASKS.md | DONE (2025-10-19) | Authority Core & Storage Guild | AUTHSTORAGE-MONGO-08-001 | Harden Authority Mongo usage<br>Scoped Mongo sessions with majority read/write concerns wired through stores and GraphQL/HTTP pipelines; replica-set election regression validated. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-MONGO-08-001 | Causal consistency for Excititor repositories<br>Session-scoped repositories shipped with new Mongo records, orchestrators/workers now share scoped sessions, and replica-set failover coverage added via `dotnet test src/StellaOps.Excititor.Storage.Mongo.Tests/StellaOps.Excititor.Storage.Mongo.Tests.csproj`. |
| Sprint 8 | Platform Maintenance | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-03-001 | Statement backfill tooling shipped admin backfill endpoint, CLI hook (`stellaops excititor backfill-statements`), integration tests, and operator runbook (`docs/dev/EXCITITOR_STATEMENT_BACKFILL.md`). |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Exporter.Json/TASKS.md | DONE (2025-10-19) | Concelier Export Guild | CONCELIER-EXPORT-08-201 | Mirror bundle + domain manifest produce signed JSON aggregates for `*.stella-ops.org` mirrors. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-19) | Concelier Export Guild | CONCELIER-EXPORT-08-202 | Mirror-ready Trivy DB bundles mirror options emit per-domain manifests/metadata/db archives with deterministic digests for downstream sync. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-20) | Concelier WebService Guild | CONCELIER-WEB-08-201 | Mirror distribution endpoints expose domain-scoped index/download APIs with auth/quota. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | DOING (2025-10-19) | BE-Conn-Stella | FEEDCONN-STELLA-08-001 | Concelier mirror connector fetch mirror manifest, verify signatures, and hydrate canonical DTOs with resume support. |
| Sprint 8 | Mirror Distribution | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-MIRROR-08-001 | Managed mirror deployments for `*.stella-ops.org` Helm/Compose overlays, CDN, runbooks. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | DOING | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-002.COORD | Authority scoped-service integration handshake<br>Session scheduled for 2025-10-20 15:0016:00UTC; agenda + attendees logged in `docs/dev/authority-plugin-di-coordination.md`. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Authority/TASKS.md | DOING | Authority Core, Plugin Platform Guild | AUTH-PLUGIN-COORD-08-002 | Coordinate scoped-service adoption for Authority plug-in registrars<br>Workshop locked for 2025-10-20 15:0016:00UTC; pre-read checklist tracked in `docs/dev/authority-plugin-di-coordination.md`. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-501 | Define shared DTOs (ScanJob, ProgressEvent), error taxonomy, and deterministic ID/timestamp helpers aligning with `ARCHITECTURE_SCANNER.md` §3§4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-502 | Observability helpers (correlation IDs, logging scopes, metric namespacing, deterministic hashes) consumed by WebService/Worker. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-503 | Security utilities: Authority client factory, OpTok caching, DPoP verifier, restart-time plug-in guardrails for scanner components. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-001 | Buildx driver scaffold + handshake with Scanner.Emit (local CAS). |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-002 | OCI annotations + provenance hand-off to Attestor. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-003 | CI demo: minimal SBOM push & backend report wiring. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-004 | Stabilize descriptor nonce derivation so repeated builds emit deterministic placeholders. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-005 | Integrate determinism guard into GitHub/Gitea workflows and archive proof artifacts. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-101 | Minimal API host with Authority enforcement, health/ready endpoints, and restart-time plug-in loader per architecture §1, §4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-102 | `/api/v1/scans` submission/status endpoints with deterministic IDs, validation, and cancellation support. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Team Scanner WebService | SCANNER-WEB-09-103 | Progress streaming (SSE/JSONL) with correlation IDs and ISO-8601 UTC timestamps, documented in API reference. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-WEB-09-104 | Configuration binding for Mongo, MinIO, queue, feature flags; startup diagnostics and fail-fast policy. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Team Scanner WebService | SCANNER-POLICY-09-105 | Policy snapshot loader + schema + OpenAPI (YAML ignore rules, VEX include/exclude, vendor precedence). |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Team Scanner WebService | SCANNER-POLICY-09-106 | `/reports` verdict assembly (Feedser+Vexer+Policy) + signed response envelope. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Team Scanner WebService | SCANNER-POLICY-09-107 | Expose score inputs, config version, and quiet provenance in `/reports` JSON and signed payload. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-201 | Worker host bootstrap with Authority auth, hosted services, and graceful shutdown semantics. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-202 | Lease/heartbeat loop with retry+jitter, poison-job quarantine, structured logging. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-203 | Analyzer dispatch skeleton emitting deterministic stage progress and honoring cancellation tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-204 | Worker metrics (queue latency, stage duration, failure counts) with OpenTelemetry resource wiring. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-205 | Harden heartbeat jitter so lease safety margin stays ≥3× and cover with regression tests + optional live queue smoke run. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-001 | Policy schema + binder + diagnostics. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-002 | Policy snapshot store + revision digests. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-003 | `/policy/preview` API (image digest → projected verdict diff). |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-CORE-09-004 | Versioned scoring config with schema validation, trust table, and golden fixtures. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-CORE-09-005 | Scoring/quiet engine compute score, enforce VEX-only quiet rules, emit inputs and provenance. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-CORE-09-006 | Unknown state & confidence decay deterministic bands surfaced in policy outputs. |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-HELM-09-001 | Helm/Compose environment profiles (dev/staging/airgap) with deterministic digests. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, DevEx | DOCS-ADR-09-001 | Establish ADR process and template. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, Platform Events | DOCS-EVENTS-09-002 | Publish event schema catalog (`docs/events/`) for critical envelopes. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-301 | Mongo catalog schemas/indexes for images, layers, artifacts, jobs, lifecycle rules plus migrations. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-302 | MinIO layout, immutability policies, client abstraction, and configuration binding. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-303 | Repositories/services with dual-write feature flag, deterministic digests, TTL enforcement tests. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-401 | Queue abstraction + Redis Streams adapter with ack/claim APIs and idempotency tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-402 | Pluggable backend support (Redis, NATS) with configuration binding, health probes, failover docs. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-403 | Retry + dead-letter strategy with structured logs/metrics for offline deployments. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Web/TASKS.md | BLOCKED (2025-10-21) | UX Specialist, Angular Eng | WEB1.TRIVY-SETTINGS-TESTS | Add headless UI test run (`ng test --watch=false`) and document prerequisites once Angular tooling is chained up. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | DONE (2025-10-20) | BE-Conn-Stella | FEEDCONN-STELLA-08-001 | Concelier mirror connector fetch mirror manifest, verify signatures, and hydrate canonical DTOs with resume support. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | DONE (2025-10-20) | BE-Conn-Stella | FEEDCONN-STELLA-08-002 | Map mirror payloads into canonical advisory DTOs with provenance referencing mirror domain + original source metadata. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | DONE (2025-10-20) | BE-Conn-Stella | FEEDCONN-STELLA-08-003 | Add incremental cursor + resume support (per-export fingerprint) and document configuration for downstream Concelier instances. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | DONE (2025-10-20) | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-002.COORD | Authority scoped-service integration handshake<br>Workshop concluded 2025-10-20 15:0016:05UTC; decisions + follow-ups recorded in `docs/dev/authority-plugin-di-coordination.md`. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | DONE (2025-10-20) | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-002 | Authority plugin integration updates scoped identity-provider services with registry handles; regression coverage via scoped registrar/unit tests. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Authority/TASKS.md | DONE (2025-10-20) | Authority Core, Plugin Platform Guild | AUTH-PLUGIN-COORD-08-002 | Coordinate scoped-service adoption for Authority plug-in registrars<br>Workshop notes and follow-up backlog captured 2025-10-20 in `docs/dev/authority-plugin-di-coordination.md`. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-WEB-09-103 | Progress streaming (SSE/JSONL) with correlation IDs and ISO-8601 UTC timestamps, documented in API reference. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-POLICY-09-105 | Policy snapshot loader + schema + OpenAPI (YAML ignore rules, VEX include/exclude, vendor precedence). |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-POLICY-09-106 | `/reports` verdict assembly (Feedser+Vexer+Policy) + signed response envelope. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-POLICY-09-107 | Expose score inputs, config version, and quiet provenance in `/reports` JSON and signed payload. |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | DONE (2025-10-21) | DevOps Guild, Scanner WebService Guild | DEVOPS-SCANNER-09-204 | Surface `SCANNER__EVENTS__*` env config across Compose/Helm and document overrides. |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | DONE (2025-10-21) | DevOps Guild, Notify Guild | DEVOPS-SCANNER-09-205 | Notify smoke job validates Redis stream + Notify deliveries after staging deploys. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE (2025-10-19) | Policy Guild | POLICY-CORE-09-004 | Versioned scoring config with schema validation, trust table, and golden fixtures. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE (2025-10-19) | Policy Guild | POLICY-CORE-09-005 | Scoring/quiet engine compute score, enforce VEX-only quiet rules, emit inputs and provenance. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE (2025-10-19) | Policy Guild | POLICY-CORE-09-006 | Unknown state & confidence decay deterministic bands surfaced in policy outputs. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | TODO | Scanner Cache Guild | SCANNER-CACHE-10-101 | Implement layer cache store keyed by layer digest with metadata retention per architecture §3.3. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | TODO | Scanner Cache Guild | SCANNER-CACHE-10-102 | Build file CAS with dedupe, TTL enforcement, and offline import/export hooks. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | TODO | Scanner Cache Guild | SCANNER-CACHE-10-103 | Expose cache metrics/logging and configuration toggles for warm/cold thresholds. |
@@ -244,15 +66,13 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-605 | Emit BOM-Index sidecar schema/fixtures (CRITICAL PATH for SP16). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-606 | Usage view bit flags integrated with EntryTrace. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-607 | Embed scoring inputs, confidence band, and quiet provenance in CycloneDX/DSSE artifacts. |
| Sprint 10 | Benchmarks | bench/TASKS.md | TODO | Bench Guild, Scanner Team | BENCH-SCANNER-10-001 | Analyzer microbench harness + baseline CSV. |
| Sprint 10 | Benchmarks | bench/TASKS.md | DONE (2025-10-21) | Bench Guild, Language Analyzer Guild | BENCH-SCANNER-10-002 | Wire real language analyzers into bench harness & refresh baselines post-implementation. |
| Sprint 10 | Samples | samples/TASKS.md | TODO | Samples Guild, Scanner Team | SAMPLES-10-001 | Sample images with SBOM/BOM-Index sidecars. |
| Sprint 10 | DevOps Security | ops/devops/TASKS.md | DONE (2025-10-20) | DevOps Guild | DEVOPS-SEC-10-301 | Address NU1902/NU1903 advisories for `MongoDB.Driver` 2.12.0 and `SharpCompress` 0.23.0; Wave0A prerequisites confirmed complete before remediation work. |
| Sprint 10 | DevOps Perf | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-PERF-10-001 | Perf smoke job ensuring <5s SBOM compose. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Authority/TASKS.md | DOING (2025-10-19) | Authority Core & Security Guild | AUTH-DPOP-11-001 | Implement DPoP proof validation + nonce handling for high-value audiences per architecture. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Authority/TASKS.md | DOING (2025-10-19) | Authority Core & Security Guild | AUTH-MTLS-11-002 | Add OAuth mTLS client credential support with certificate-bound tokens and introspection updates. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | TODO | Signer Guild | SIGNER-API-11-101 | `/sign/dsse` pipeline with Authority auth, PoE introspection, release verification, DSSE signing. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | TODO | Signer Guild | SIGNER-REF-11-102 | `/verify/referrers` endpoint with OCI lookup, caching, and policy enforcement. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | TODO | Signer Guild | SIGNER-QUOTA-11-103 | Enforce plan quotas, concurrency/QPS limits, artifact size caps with metrics/audit logs. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | DONE (2025-10-21) | Signer Guild | SIGNER-API-11-101 | `/sign/dsse` pipeline with Authority auth, PoE introspection, release verification, DSSE signing. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | DONE (2025-10-21) | Signer Guild | SIGNER-REF-11-102 | `/verify/referrers` endpoint with OCI lookup, caching, and policy enforcement. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | DONE (2025-10-21) | Signer Guild | SIGNER-QUOTA-11-103 | Enforce plan quotas, concurrency/QPS limits, artifact size caps with metrics/audit logs. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Attestor/TASKS.md | TODO | Attestor Guild | ATTESTOR-API-11-201 | `/rekor/entries` submission pipeline with dedupe, proof acquisition, and persistence. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Attestor/TASKS.md | TODO | Attestor Guild | ATTESTOR-VERIFY-11-202 | `/rekor/verify` + retrieval endpoints validating signatures and Merkle proofs. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Attestor/TASKS.md | TODO | Attestor Guild | ATTESTOR-OBS-11-203 | Telemetry, alerting, mTLS hardening, and archive workflow for Attestor. |
@@ -268,8 +88,11 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Webhook/TASKS.md | TODO | Zastava Webhook Guild | ZASTAVA-WEBHOOK-12-101 | Admission controller host with TLS bootstrap and Authority auth. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Webhook/TASKS.md | TODO | Zastava Webhook Guild | ZASTAVA-WEBHOOK-12-102 | Query Scanner `/policy/runtime`, resolve digests, enforce verdicts. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Webhook/TASKS.md | TODO | Zastava Webhook Guild | ZASTAVA-WEBHOOK-12-103 | Caching, fail-open/closed toggles, metrics/logging for admission decisions. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-301 | `/runtime/events` ingestion endpoint with validation, batching, storage hooks. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-302 | `/policy/runtime` endpoint joining SBOM baseline + policy verdict with TTL guidance. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-20) | Scanner WebService Guild | SCANNER-RUNTIME-12-301 | `/runtime/events` ingestion endpoint with validation, batching, storage hooks. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | DOING (2025-10-20) | Scanner WebService Guild | SCANNER-RUNTIME-12-302 | `/policy/runtime` endpoint joining SBOM baseline + policy verdict, returning admission guidance. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-303 | Align `/policy/runtime` verdicts with canonical policy evaluation (Feedser/Vexer). |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-304 | Integrate attestation verification into runtime policy metadata. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-305 | Deliver shared fixtures + e2e validation with Zastava/CLI teams. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-AUTH-13-001 | Integrate Authority OIDC + DPoP flows with session management. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-SCANS-13-002 | Build scans module (list/detail/SBOM/diff/attestation) with performance + accessibility targets. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-VEX-13-003 | Implement VEX explorer + policy editor with preview integration. |
@@ -277,7 +100,7 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-SCHED-13-005 | Scheduler panel: schedules CRUD, run history, dry-run preview. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | DOING (2025-10-19) | UI Guild | UI-NOTIFY-13-006 | Notify panel: channels/rules CRUD, deliveries view, test send. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO | DevEx/CLI | CLI-RUNTIME-13-005 | Add runtime policy test verbs that consume `/policy/runtime` and display verdicts. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO | DevEx/CLI | CLI-OFFLINE-13-006 | Implement offline kit pull/import/status commands with integrity checks. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | DONE (2025-10-21) | DevEx/CLI | CLI-OFFLINE-13-006 | Implement offline kit pull/import/status commands with integrity checks. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO | DevEx/CLI | CLI-PLUGIN-13-007 | Package non-core CLI verbs as restart-time plug-ins (manifest + loader tests). |
| Sprint 14 | Release & Offline Ops | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-REL-14-001 | Deterministic build/release pipeline with SBOM/provenance, signing, and manifest generation. |
| Sprint 14 | Release & Offline Ops | ops/offline-kit/TASKS.md | TODO | Offline Kit Guild | DEVOPS-OFFLINE-14-002 | Offline kit packaging workflow with integrity verification and documentation. |
@@ -298,39 +121,28 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Engine/TASKS.md | TODO | Notify Engine Guild | NOTIFY-ENGINE-15-304 | Test-send sandbox + preview utilities. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | TODO | Notify WebService Guild | NOTIFY-WEB-15-101 | Minimal API host with Authority enforcement and plug-in loading. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | TODO | Notify WebService Guild | NOTIFY-WEB-15-102 | Rules/channel/template CRUD with audit logging. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | DONE (2025-10-19) | Notify WebService Guild | NOTIFY-WEB-15-103 | Delivery history & test-send endpoints. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | TODO | Notify WebService Guild | NOTIFY-WEB-15-104 | Configuration binding + startup diagnostics. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-201 | Bus subscription + leasing loop with backoff. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-202 | Rules evaluation pipeline integration. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-203 | Channel dispatch orchestration with retries. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-204 | Metrics/telemetry for Notify workers. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-501 | Slack connector with rate-limit aware delivery. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-502 | Slack health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-601 | Teams connector with Adaptive Cards. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-602 | Teams health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-701 | SMTP connector with TLS + rendering. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-702 | DKIM + health/test-send flows. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | BLOCKED (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-702 | DKIM + health/test-send flows. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-801 | Webhook connector with signing/retries. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-802 | Webhook health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-503 | Package Slack connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-603 | Package Teams connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-703 | Package Email connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DOING (2025-10-19) | Scanner WebService Guild | SCANNER-EVENTS-15-201 | Emit `scanner.report.ready` + `scanner.scan.completed` events. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | BLOCKED (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-802 | Webhook health/test-send support. |
| Sprint 16 | Notify Foundations | src/StellaOps.Scanner.WebService/TASKS.md | BLOCKED (2025-10-20) | Scanner WebService Guild | SCANNER-EVENTS-16-301 | Redis publisher integration tests once Notify queue adapter ships. |
| Sprint 15 | Benchmarks | bench/TASKS.md | TODO | Bench Guild, Notify Team | BENCH-NOTIFY-15-001 | Notify dispatch throughput bench with results CSV. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-803 | Package Webhook connector as restart-time plug-in (manifest + host registration). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | TODO | Scheduler Models Guild | SCHED-MODELS-16-101 | Define Scheduler DTOs & validation. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | TODO | Scheduler Models Guild | SCHED-MODELS-16-102 | Publish schema docs/sample payloads. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | TODO | Scheduler Models Guild | SCHED-MODELS-16-103 | Versioning/migration helpers for schedules/runs. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Storage.Mongo/TASKS.md | TODO | Scheduler Storage Guild | SCHED-STORAGE-16-201 | Mongo schemas/indexes for Scheduler state. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Storage.Mongo/TASKS.md | TODO | Scheduler Storage Guild | SCHED-STORAGE-16-202 | Repositories with tenant scoping, TTL, causal consistency. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Storage.Mongo/TASKS.md | TODO | Scheduler Storage Guild | SCHED-STORAGE-16-203 | Audit + stats materialization for UI. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | TODO | Scheduler Queue Guild | SCHED-QUEUE-16-401 | Queue abstraction + Redis Streams adapter. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | TODO | Scheduler Queue Guild | SCHED-QUEUE-16-402 | NATS JetStream adapter with health probes. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | TODO | Scheduler Queue Guild | SCHED-QUEUE-16-403 | Dead-letter handling + metrics. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | DONE (2025-10-20) | Scheduler Queue Guild | SCHED-QUEUE-16-403 | Dead-letter handling + metrics. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | TODO | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-301 | Ingest BOM-Index into roaring bitmap store. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | TODO | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-302 | Query APIs for ResolveByPurls/ResolveByVulns/ResolveAll. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | TODO | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-303 | Snapshot/compaction/invalidation workflow. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | DOING | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-300 | **STUB** ImpactIndex ingest/query using fixtures (to be removed by SP16 completion). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-101 | Minimal API host with Authority enforcement. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-102 | Schedules CRUD (cron validation, pause/resume, audit). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-103 | Runs API (list/detail/cancel) + impact previews. |
@@ -346,3 +158,4 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
| Sprint 17 | Symbol Intelligence & Forensics | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-17-401 | Persist runtime build-id observations and expose them for debug-symbol correlation. |
| Sprint 17 | Symbol Intelligence & Forensics | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-REL-17-002 | Ship stripped debug artifacts organised by build-id within release/offline kits. |
| Sprint 17 | Symbol Intelligence & Forensics | docs/TASKS.md | TODO | Docs Guild | DOCS-RUNTIME-17-004 | Document build-id workflows for SBOMs, runtime events, and debug-store usage. |
| Sprint 18 | Launch Readiness | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-LAUNCH-18-001 | Production launch cutover rehearsal and runbook publication (blocked on implementation sign-off and environment setup). |

View File

@@ -1,427 +0,0 @@
This file describe implementation of Stella Ops (docs/README.md). Implementation must respect rules from AGENTS.md (read if you have not).
| Sprint | Theme | Tasks File Path | Status | Type of Specialist | Task ID | Task Description |
| --- | --- | --- | --- | --- | --- | --- |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-001 | SemVer primitive range-style metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md. This task lays the groundwork—complete the SemVer helper updates before teammates pick up FEEDMODELS-SCHEMA-01-002/003 and FEEDMODELS-SCHEMA-02-900. Use ./src/FASTER_MODELING_AND_NORMALIZATION.md for the target rule structure. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-002 | Provenance decision rationale field<br>Instructions to work:<br>AdvisoryProvenance now carries `decisionReason` and docs/tests were updated. Connectors and merge tasks should populate the field when applying precedence/freshness/tie-breaker logic; see src/StellaOps.Concelier.Models/PROVENANCE_GUIDELINES.md for usage guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-003 | Normalized version rules collection<br>Instructions to work:<br>`AffectedPackage.NormalizedVersions` and supporting comparer/docs/tests shipped. Connector owners must emit rule arrays per ./src/FASTER_MODELING_AND_NORMALIZATION.md and report progress via FEEDMERGE-COORD-02-900 so merge/storage backfills can proceed. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-02-900 | Range primitives for SemVer/EVR/NEVRA metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md before resuming this stalled effort. Confirm helpers align with the new `NormalizedVersions` representation so connectors finishing in Sprint 2 can emit consistent metadata. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>Shared `SemVerRangeRuleBuilder` now outputs primitives + normalized rules per `FASTER_MODELING_AND_NORMALIZATION.md`; CVE/GHSA connectors consuming the API have verified fixtures. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>AdvisoryStore dual-writes flattened `normalizedVersions` when `concelier.storage.enableSemVerStyle` is set; migration `20251011-semver-style-backfill` updates historical records and docs outline the rollout. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence<br>Storage now persists `provenance.decisionReason` for advisories and merge events; tests cover round-trips. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Bootstrapper seeds compound/sparse indexes for flattened normalized rules and `docs/dev/mongo_indices.md` documents query guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Updated constructors/tests keep storage suites passing with the new feature flag defaults. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Install/runbooks document connected vs air-gapped resilience profiles and monitoring hooks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Operator guides now call out `route/status/subject/clientId/scopes/bypass/remote` audit fields and SIEM triggers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and links audit signals to the rollout checklist. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.HOST | Rate limiter policy binding<br>Authority host now applies configuration-driven fixed windows to `/token`, `/authorize`, and `/internal/*`; integration tests assert 429 + `Retry-After` headers; docs/config samples refreshed for Docs guild diagrams. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.BUILD | Authority rate-limiter follow-through<br>`Security.RateLimiting` now fronts token/authorize/internal limiters; Authority + Configuration matrices (`dotnet test src/StellaOps.Authority/StellaOps.Authority.sln`, `dotnet test src/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj`) passed on 2025-10-11; awaiting #authority-core broadcast. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHCORE-BUILD-OPENIDDICT / AUTHCORE-STORAGE-DEVICE-TOKENS / AUTHCORE-BOOTSTRAP-INVITES | Address remaining Authority compile blockers (OpenIddict transaction shim, token device document, bootstrap invite cleanup) so `dotnet build src/StellaOps.Authority.sln` returns success. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | PLG6.DOC | Plugin developer guide polish<br>Section 9 now documents rate limiter metadata, config keys, and lockout interplay; YAML samples updated alongside Authority config templates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DOING (2025-10-14) | Team WebService & Authority | SEC2.PLG | Emit audit events from password verification outcomes and persist via `IAuthorityLoginAttemptStore`; Serilog enrichment complete, storage durability tests in flight. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DOING (2025-10-14) | Team WebService & Authority | SEC3.PLG | Ensure lockout responses carry rate-limit metadata through plugin logs/events; retry-after propagation and limiter tests underway. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DOING (2025-10-14) | Team WebService & Authority | SEC5.PLG | Address plugin-specific mitigations in threat model backlog; mitigation items tracked, docs updates pending. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | BLOCKED (2025-10-12) | Team WebService & Authority | PLG4-6.CAPABILITIES | Finalise capability metadata exposure and docs once Authority rate-limiter stream (CORE8/SEC3) is stable; awaiting dependency unblock. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | TODO | Team WebService & Authority | PLG6.DIAGRAM | Export final sequence/component diagrams for the developer guide and add offline-friendly assets under `docs/assets/authority`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | REVIEW (2025-10-13) | Team WebService & Authority | PLG7.RFC | Socialize LDAP plugin RFC and capture guild feedback; awaiting final review sign-off and follow-up issue tracking. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-001 | Fetch pipeline & state tracking<br>Summary planner now drives monthly/yearly VINCE fetches, persists pending summaries/notes, and hydrates VINCE detail queue with telemetry.<br>Team instructions: Read ./AGENTS.md and src/StellaOps.Concelier.Connector.CertCc/AGENTS.md. Coordinate daily with Models/Merge leads so new normalizedVersions output and provenance tags stay aligned with ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-002 | VINCE note detail fetcher<br>Summary planner queues VINCE note detail endpoints, persists raw JSON with SHA/ETag metadata, and records retry/backoff metrics. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-003 | DTO & parser implementation<br>Added VINCE DTO aggregate, Markdown→text sanitizer, vendor/status/vulnerability parsers, and parser regression fixture. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-004 | Canonical mapping & range primitives<br>VINCE DTO aggregate flows through `CertCcMapper`, emitting vendor range primitives + normalized version rules that persist via `_advisoryStore`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-005 | Deterministic fixtures/tests<br>Snapshot harness refreshed 2025-10-12; `certcc-*.snapshot.json` regenerated and regression suite green without UPDATE flag drift. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-006 | Telemetry & documentation<br>`CertCcDiagnostics` publishes summary/detail/parse/map metrics (meter `StellaOps.Concelier.Connector.CertCc`), README documents instruments, and log guidance captured for Ops on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-007 | Connector test harness remediation<br>Harness now wires `AddSourceCommon`, resets `FakeTimeProvider`, and passes canned-response regression run dated 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-008 | Snapshot coverage handoff<br>Fixtures regenerated with normalized ranges + provenance fields on 2025-10-11; QA handoff notes published and merge backfill unblocked. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-012 | Schema sync & snapshot regen follow-up<br>Fixtures regenerated with normalizedVersions + provenance decision reasons; handoff notes updated for Merge backfill 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-009 | Detail/map reintegration plan<br>Staged reintegration plan published in `src/StellaOps.Concelier.Connector.CertCc/FEEDCONN-CERTCC-02-009_PLAN.md`; coordinates enablement with FEEDCONN-CERTCC-02-004. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-010 | Partial-detail graceful degradation<br>Detail fetch now tolerates 404/403/410 responses and regression tests cover mixed endpoint availability. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Distro.RedHat/TASKS.md | DOING (2025-10-10) | Team Connector Resumption CERT/RedHat | FEEDCONN-REDHAT-02-001 | Fixture validation sweep<br>Instructions to work:<br>Regenerating RHSA fixtures awaits remaining range provenance patches; review snapshot diffs and update docs once upstream helpers land. Conflict resolver deltas logged in src/StellaOps.Concelier.Connector.Distro.RedHat/CONFLICT_RESOLVER_NOTES.md for Sprint 3 consumers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-001 | Canonical mapping & range primitives<br>Mapper emits SemVer rules (`scheme=apple:*`); fixtures regenerated with trimmed references + new RSR coverage, update tooling finalized. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-002 | Deterministic fixtures/tests<br>Sanitized live fixtures + regression snapshots wired into tests; normalized rule coverage asserted. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-003 | Telemetry & documentation<br>Apple meter metrics wired into Concelier WebService OpenTelemetry configuration; README and fixtures document normalizedVersions coverage. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-004 | Live HTML regression sweep<br>Sanitised HT125326/HT125328/HT106355/HT214108/HT215500 fixtures recorded and regression tests green on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-005 | Fixture regeneration tooling<br>`UPDATE_APPLE_FIXTURES=1` flow fetches & rewrites fixtures; README documents usage.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Connector.Vndr.Apple/AGENTS.md. Resume stalled tasks, ensuring normalizedVersions output and fixtures align with ./src/FASTER_MODELING_AND_NORMALIZATION.md before handing data to the conflict sprint. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance<br>Team instructions: Read ./AGENTS.md and each module's AGENTS file. Adopt the `NormalizedVersions` array emitted by the models sprint, wiring provenance `decisionReason` where merge overrides occur. Follow ./src/FASTER_MODELING_AND_NORMALIZATION.md; report via src/StellaOps.Concelier.Merge/TASKS.md (FEEDMERGE-COORD-02-900). Progress 2025-10-11: GHSA/OSV emit normalized arrays with refreshed fixtures; CVE mapper now surfaces SemVer normalized ranges; NVD/KEV adoption pending; outstanding follow-ups include FEEDSTORAGE-DATA-02-001, FEEDMERGE-ENGINE-02-002, and rolling `tools/FixtureUpdater` updates across connectors. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-CVE-02-003 | CVE normalized versions uplift |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-KEV-02-003 | KEV normalized versions propagation |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-04-003 | OSV parity fixture refresh |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DOING (2025-10-10) | Team WebService & Authority | FEEDWEB-DOCS-01-001 | Document authority toggle & scope requirements<br>Quickstart updates are staged; awaiting Docs guild review before publishing operator guide refresh. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Operator docs now outline connected vs air-gapped resilience profiles and monitoring cues. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Audit logging guidance highlights `route/status/subject/clientId/scopes/bypass/remote` fields and SIEM alerts. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and ties audit signals to rollout checks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-19) | Team WebService & Authority | FEEDWEB-OPS-01-006 | Rename plugin drop directory to namespaced path<br>Build outputs now point at `StellaOps.Concelier.PluginBinaries`/`StellaOps.Authority.PluginBinaries`; defaults/docs/tests updated to reflect the new layout. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | BLOCKED (2025-10-10) | Team WebService & Authority | FEEDWEB-OPS-01-007 | Authority resilience adoption<br>Roll out retry/offline knobs to deployment docs and align CLI parity once LIB5 resilience options land; unblock when library release is available and docs review completes. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCORE-ENGINE-01-001 | CORE8.RL — Rate limiter plumbing validated; integration tests green and docs handoff recorded for middleware ordering + Retry-After headers (see `docs/dev/authority-rate-limit-tuning-outline.md` for continuing guidance). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCRYPTO-ENGINE-01-001 | SEC3.A — Shared metadata resolver confirmed via host test run; SEC3.B now unblocked for tuning guidance (outline captured in `docs/dev/authority-rate-limit-tuning-outline.md`). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-13) | Team Authority Platform & Security Guild | AUTHSEC-DOCS-01-002 | SEC3.B — Published `docs/security/rate-limits.md` with tuning matrix, alert thresholds, and lockout interplay guidance; Docs guild can lift copy into plugin guide. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHSEC-CRYPTO-02-001 | SEC5.B1 — Introduce libsodium signing provider and parity tests to unblock CLI verification enhancements. |
| Sprint 9 | Sovereign Crypto Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-19) | Security Guild | SEC6.A | BouncyCastle-backed Ed25519 signing plug-in wired via `ICryptoProviderRegistry`; Scanner WebService now resolves signing through the registry; AGENTS updated to enforce plug-in rule. |
| Sprint 1 | Bootstrap & Replay Hardening | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Security Guild | AUTHSEC-CRYPTO-02-004 | SEC5.D/E — Finish bootstrap invite lifecycle (API/store/cleanup) and token device heuristics; build currently red due to pending handler integration. |
| Sprint 1 | Developer Tooling | src/StellaOps.Cli/TASKS.md | DONE (2025-10-15) | DevEx/CLI | AUTHCLI-DIAG-01-001 | Surface password policy diagnostics in CLI startup/output so operators see weakened overrides immediately.<br>CLI now loads Authority plug-ins at startup, logs weakened password policies (length/complexity), and regression coverage lives in `StellaOps.Cli.Tests/Services/AuthorityDiagnosticsReporterTests`. |
| Sprint 1 | Developer Tooling | src/StellaOps.Cli/TASKS.md | TODO Display export metadata (sha256, size, Rekor link), support optional artifact download path, and handle cache hits gracefully. | DevEx/CLI | EXCITITOR-CLI-01-002 | EXCITITOR-CLI-01-002 Export download & attestation UX |
| Sprint 1 | Developer Tooling | src/StellaOps.Cli/TASKS.md | TODO Update docs/09_API_CLI_REFERENCE.md and quickstart snippets to cover Excititor verbs, offline guidance, and attestation verification workflow. | Docs/CLI | EXCITITOR-CLI-01-003 | EXCITITOR-CLI-01-003 CLI docs & examples for Excititor |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHPLUG-DOCS-01-001 | PLG6.DOC — Developer guide copy + diagrams merged 2025-10-11; limiter guidance incorporated and handed to Docs guild for asset export. |
| Sprint 1 | Backlog | src/StellaOps.Web/TASKS.md | TODO | UX Specialist, Angular Eng | WEB1.TRIVY-SETTINGS | Implement Trivy DB exporter settings panel with `publishFull`, `publishDelta`, `includeFull`, `includeDelta` toggles and “Run export now” action using future `/exporters/trivy-db/settings` API. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-12) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>`SemVerRangeRuleBuilder` shipped 2025-10-12 with comparator/` |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Indexes seeded + docs updated 2025-10-11 to cover flattened normalized rules for connector adoption. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDMERGE-ENGINE-02-002 | Normalized versions union & dedupe<br>Affected package resolver unions/dedupes normalized rules, stamps merge provenance with `decisionReason`, and tests cover the rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Merge/TASKS.md | DOING (2025-10-12) | Team Merge & QA Enforcement | FEEDMERGE-COORD-02-900 | Range primitives rollout coordination<br>Coordinate remaining connectors (`Acsc`, `Cccs`, `CertBund`, `CertCc`, `Cve`, `Ghsa`, `Ics.Cisa`, `Kisa`, `Ru.Bdu`, `Ru.Nkcki`, `Vndr.Apple`, `Vndr.Cisco`, `Vndr.Msrc`) to emit canonical range primitives with provenance tags; fixtures tracked in `RANGE_PRIMITIVES_COORDINATION.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-004 | GHSA credits & ecosystem severity mapping |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-005 | GitHub quota monitoring & retries |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-006 | Production credential & scheduler rollout |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-007 | Credit parity regression fixtures |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-004 | NVD CVSS & CWE precedence payloads |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-005 | NVD merge/export parity regression |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-004 | OSV references & credits alignment |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-005 | Fixture updater workflow<br>Resolved 2025-10-12: OSV mapper now derives canonical PURLs for Go + scoped npm packages when raw payloads omit `purl`; conflict fixtures unchanged for invalid npm names. Verified via `dotnet test src/StellaOps.Concelier.Connector.Osv.Tests`, `src/StellaOps.Concelier.Connector.Ghsa.Tests`, `src/StellaOps.Concelier.Connector.Nvd.Tests`, and backbone normalization/storage suites. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Acsc/TASKS.md | DONE (2025-10-12) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ACSC-02-001 … 02-008 | Fetch→parse→map pipeline, fixtures, diagnostics, and README finished 2025-10-12; downstream export parity captured via FEEDEXPORT-JSON-04-001 / FEEDEXPORT-TRIVY-04-001 (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Acsc/TASKS.md | **DONE (2025-10-11)** Reproduced Akamai resets, drafted downgrade plan (two-stage HTTP/2 retry + relay fallback), and filed `FEEDCONN-SHARED-HTTP2-001`; module README TODO will host the per-environment knob matrix. | BE-Conn-ACSC | FEEDCONN-ACSC-02-008 | FEEDCONN-ACSC-02-008 HTTP client compatibility plan |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cccs/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CCCS-02-001 … 02-008 | Observability meter, historical harvest plan, and DOM sanitizer refinements wrapped; ops notes live under `docs/ops/concelier-cccs-operations.md` with fixtures validating EN/FR list handling. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.CertBund/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CERTBUND-02-001 … 02-008 | Telemetry/docs (02-006) and history/locale sweep (02-007) completed alongside pipeline; runbook `docs/ops/concelier-certbund-operations.md` captures locale guidance and offline packaging. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kisa/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-KISA-02-001 … 02-007 | Connector, tests, and telemetry/docs (02-006) finalized; localisation notes in `docs/dev/kisa_connector_notes.md` complete rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Bdu/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-RUBDU-02-001 … 02-008 | Fetch/parser/mapper refinements, regression fixtures, telemetry/docs, access options, and trusted root packaging all landed; README documents offline access strategy. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Nkcki/TASKS.md | DONE (2025-10-13) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-NKCKI-02-001 … 02-008 | Listing fetch, parser, mapper, fixtures, telemetry/docs, and archive plan finished; Mongo2Go/libcrypto dependency resolved via bundled OpenSSL noted in ops guide. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ICSCISA-02-001 … 02-011 | Feed parser attachment fixes, SemVer exact values, regression suites, telemetry/docs updates, and handover complete; ops runbook now details attachment verification + proxy usage. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CISCO-02-001 … 02-007 | OAuth fetch pipeline, DTO/mapping, tests, and telemetry/docs shipped; monitoring/export integration follow-ups recorded in Ops docs and exporter backlog (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Msrc/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-MSRC-02-001 … 02-008 | Azure AD onboarding (02-008) unblocked fetch/parse/map pipeline; fixtures, telemetry/docs, and Offline Kit guidance published in `docs/ops/concelier-msrc-operations.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-15) | Team Connector Support & Monitoring | FEEDCONN-CVE-02-001 … 02-002 | CVE data-source selection, fetch pipeline, and docs landed 2025-10-10. 2025-10-15: smoke verified using the seeded mirror fallback; connector now logs a warning and pulls from `seed-data/cve/` until live CVE Services credentials arrive. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Support & Monitoring | FEEDCONN-KEV-02-001 … 02-002 | KEV catalog ingestion, fixtures, telemetry, and schema validation completed 2025-10-12; ops dashboard published. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-01-001 | Canonical schema docs refresh<br>Updated canonical schema + provenance guides with SemVer style, normalized version rules, decision reason change log, and migration notes. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-001 | Concelier-SemVer Playbook<br>Published merge playbook covering mapper patterns, dedupe flow, indexes, and rollout checklist. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-002 | Normalized versions query guide<br>Delivered Mongo index/query addendum with `$unwind` recipes, dedupe checks, and operational checklist.<br>Instructions to work:<br>DONE Read ./AGENTS.md and docs/AGENTS.md. Document every schema/index/query change produced in Sprint 1-2 leveraging ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | REVIEW | Docs Guild, Plugin Team | DOC4.AUTH-PDG | Copy-edit `docs/dev/31_AUTHORITY_PLUGIN_DEVELOPER_GUIDE.md`, export lifecycle diagram, add LDAP RFC cross-link. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-001 | Canonical merger implementation<br>`CanonicalMerger` ships with freshness/tie-breaker logic, provenance, and unit coverage feeding Merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-002 | Field precedence and tie-breaker map<br>Field precedence tables and tie-breaker metrics wired into the canonical merge flow; docs/tests updated.<br>Instructions to work:<br>Read ./AGENTS.md and core AGENTS. Implement the conflict resolver exactly as specified in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md, coordinating with Merge and Storage teammates. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-03-001 | Merge event provenance audit prep<br>Merge events now persist `fieldDecisions` and analytics-ready provenance snapshots. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>Dual-write/backfill flag delivered; migration + options validated in tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Storage tests adjusted for normalized versions/decision reasons.<br>Instructions to work:<br>Read ./AGENTS.md and storage AGENTS. Extend merge events with decision reasons and analytics views to support the conflict rules, and deliver the dual-write/backfill for `NormalizedVersions` + `decisionReason` so connectors can roll out safely. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-001 | GHSA/NVD/OSV conflict rules<br>Merge pipeline consumes `CanonicalMerger` output prior to precedence merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-002 | Override metrics instrumentation<br>Merge events capture per-field decisions; counters/logs align with conflict rules. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-003 | Reference & credit union pipeline<br>Canonical merge preserves unions with updated tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-QA-04-001 | End-to-end conflict regression suite<br>Added regression tests (`AdvisoryMergeServiceTests`) covering canonical + precedence flow.<br>Instructions to work:<br>Read ./AGENTS.md and merge AGENTS. Integrate the canonical merger, instrument metrics, and deliver comprehensive regression tests following ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-002 | GHSA conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-NVD-04-002 | NVD conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-OSV-04-002 | OSV conflict regression fixtures<br>Instructions to work:<br>Read ./AGENTS.md and module AGENTS. Produce fixture triples supporting the precedence/tie-breaker paths defined in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md and hand them to Merge QA. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-11) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-001 | Concelier Conflict Rules<br>Runbook published at `docs/ops/concelier-conflict-resolution.md`; metrics/log guidance aligned with Sprint 3 merge counters. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-16) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-002 | Conflict runbook ops rollout<br>Ops review completed, alert thresholds applied, and change log appended in `docs/ops/concelier-conflict-resolution.md`; task closed after connector signals verified. |
| Sprint 3 | Backlog | src/StellaOps.Concelier.Connector.Common/TASKS.md | **TODO (2025-10-15)** Provide a reusable CLI/utility to seed `pendingDocuments`/`pendingMappings` for connectors (MSRC backfills require scripted CVRF + detail injection). Coordinate with MSRC team for expected JSON schema and handoff once prototype lands. | Tools Guild, BE-Conn-MSRC | FEEDCONN-SHARED-STATE-003 | FEEDCONN-SHARED-STATE-003 Source state seeding helper |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-15) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-04-001 | Advisory schema parity (description/CWE/canonical metric)<br>Extend `Advisory` and related records with description text, CWE collection, and canonical metric pointer; refresh validation + serializer determinism tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-003 | Canonical merger parity for new fields<br>Teach `CanonicalMerger` to populate description, CWEResults, and canonical metric pointer with provenance + regression coverage. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-004 | Reference normalization & freshness instrumentation cleanup<br>Implement URL normalization for reference dedupe, align freshness-sensitive instrumentation, and add analytics tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-004 | Merge pipeline parity for new advisory fields<br>Ensure merge service + merge events surface description/CWE/canonical metric decisions with updated metrics/tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-005 | Connector coordination for new advisory fields<br>GHSA/NVD/OSV connectors now ship description, CWE, and canonical metric data with refreshed fixtures; merge coordination log updated and exporters notified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.Json/TASKS.md | DONE (2025-10-15) | Team Exporters JSON | FEEDEXPORT-JSON-04-001 | Surface new advisory fields in JSON exporter<br>Update schemas/offline bundle + fixtures once model/core parity lands.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.Json.Tests` validated canonical metric/CWE emission. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-15) | Team Exporters Trivy DB | FEEDEXPORT-TRIVY-04-001 | Propagate new advisory fields into Trivy DB package<br>Extend Bolt builder, metadata, and regression tests for the expanded schema.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.TrivyDb.Tests` confirmed canonical metric/CWE propagation. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-16) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-004 | Harden CVSS fallback so canonical metric ids persist when GitHub omits vectors; extend fixtures and document severity precedence hand-off to Merge. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-16) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-04-005 | Map OSV advisories lacking CVSS vectors to canonical metric ids/notes and document CWE provenance quirks; schedule parity fixture updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-001 | Stand up canonical VEX claim/consensus records with deterministic serializers so Storage/Exports share a stable contract. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-002 | Implement trust-weighted consensus resolver with baseline policy weights, justification gates, telemetry output, and majority/tie handling. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-003 | Publish shared connector/exporter/attestation abstractions and deterministic query signature utilities for cache/attestation workflows. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-001 | Established policy options & snapshot provider covering baseline weights/overrides. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-002 | Policy evaluator now feeds consensus resolver with immutable snapshots. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-003 | Author policy diagnostics, CLI/WebService surfacing, and documentation updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-004 | Implement YAML/JSON schema validation and deterministic diagnostics for operator bundles. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-005 | Add policy change tracking, snapshot digests, and telemetry/logging hooks. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-15) | Team Excititor Storage | EXCITITOR-STORAGE-01-001 | Mongo mapping registry plus raw/export entities and DI extensions in place. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-16) | Team Excititor Storage | EXCITITOR-STORAGE-01-004 | Build provider/consensus/cache class maps and related collections. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-15) | Team Excititor Export | EXCITITOR-EXPORT-01-001 | Export engine delivers cache lookup, manifest creation, and policy integration. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-17) | Team Excititor Export | EXCITITOR-EXPORT-01-004 | Connect export engine to attestation client and persist Rekor metadata. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-001 | Implement in-toto predicate + DSSE builder providing envelopes for export attestation. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | TODO Add verification helpers for Worker/WebService, metrics/logging hooks, and negative-path regression tests. | Team Excititor Attestation | EXCITITOR-ATTEST-01-003 | EXCITITOR-ATTEST-01-003 Verification suite & observability |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Connectors.Abstractions/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors | EXCITITOR-CONN-ABS-01-001 | Deliver shared connector context/base classes so provider plug-ins can be activated via WebService/Worker. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-17) | Team Excititor WebService | EXCITITOR-WEB-01-001 | Scaffold minimal API host, DI, and `/excititor/status` endpoint integrating policy, storage, export, and attestation services. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.WebService/TASKS.md | TODO Implement `/excititor/init`, `/excititor/ingest/run`, `/excititor/ingest/resume`, `/excititor/reconcile` with token scope enforcement and structured run telemetry. | Team Excititor WebService | EXCITITOR-WEB-01-002 | EXCITITOR-WEB-01-002 Ingest & reconcile endpoints |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.WebService/TASKS.md | TODO Add `/excititor/export`, `/excititor/export/{id}`, `/excititor/export/{id}/download`, `/excititor/verify`, returning artifact + attestation metadata with cache awareness. | Team Excititor WebService | EXCITITOR-WEB-01-003 | EXCITITOR-WEB-01-003 Export & verify endpoints |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Worker/TASKS.md | DONE (2025-10-17) | Team Excititor Worker | EXCITITOR-WORKER-01-001 | Create Worker host with provider scheduling and logging to drive recurring pulls/reconciliation. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Worker/TASKS.md | TODO Implement durable resume markers, exponential backoff with jitter, and quarantine for failing connectors per architecture spec. | Team Excititor Worker | EXCITITOR-WORKER-01-002 | EXCITITOR-WORKER-01-002 Resume tokens & retry policy |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Worker/TASKS.md | TODO Add scheduled attestation re-verification and cache pruning routines, surfacing metrics for export reuse ratios. | Team Excititor Worker | EXCITITOR-WORKER-01-003 | EXCITITOR-WORKER-01-003 Verification & cache GC loops |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CSAF-01-001 | Implement CSAF normalizer foundation translating provider documents into `VexClaim` entries. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CSAF/TASKS.md | TODO Normalize CSAF `product_status` + `justification` values into policy-aware enums with audit diagnostics for unsupported codes. | Team Excititor Formats | EXCITITOR-FMT-CSAF-01-002 | EXCITITOR-FMT-CSAF-01-002 Status/justification mapping |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CSAF/TASKS.md | TODO Provide CSAF export writer producing deterministic documents (per vuln/product) and manifest metadata for attestation. | Team Excititor Formats | EXCITITOR-FMT-CSAF-01-003 | EXCITITOR-FMT-CSAF-01-003 CSAF export adapter |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CYCLONE-01-001 | Implement CycloneDX VEX normalizer capturing `analysis` state and component references. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md | TODO Implement helpers to reconcile component/service references against policy expectations and emit diagnostics for missing SBOM links. | Team Excititor Formats | EXCITITOR-FMT-CYCLONE-01-002 | EXCITITOR-FMT-CYCLONE-01-002 Component reference reconciliation |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md | TODO Provide exporters producing CycloneDX VEX output with canonical ordering and hash-stable manifests. | Team Excititor Formats | EXCITITOR-FMT-CYCLONE-01-003 | EXCITITOR-FMT-CYCLONE-01-003 CycloneDX export serializer |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-OPENVEX-01-001 | Implement OpenVEX normalizer to ingest attestations into canonical claims with provenance. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md | TODO Add reducers merging multiple OpenVEX statements, resolving conflicts deterministically, and emitting policy diagnostics. | Team Excititor Formats | EXCITITOR-FMT-OPENVEX-01-002 | EXCITITOR-FMT-OPENVEX-01-002 Statement merge utilities |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md | TODO Provide export serializer generating canonical OpenVEX documents with optional SBOM references and hash-stable ordering. | Team Excititor Formats | EXCITITOR-FMT-OPENVEX-01-003 | EXCITITOR-FMT-OPENVEX-01-003 OpenVEX export writer |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-001 | Ship Red Hat CSAF provider metadata discovery enabling incremental pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-002 | Fetch CSAF windows with ETag handling, resume tokens, quarantine on schema errors, and persist raw docs. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-003 | Populate provider trust overrides (cosign issuer, identity regex) and provenance hints for policy evaluation/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-004 | Persist resume cursors (last updated timestamp/document hashes) in storage and reload during fetch to avoid duplicates. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-005 | Register connector in Worker/WebService DI, add scheduled jobs, and document CLI triggers for Red Hat CSAF pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-006 | Add CSAF normalization parity fixtures ensuring RHSA-specific metadata is preserved. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-001 | Implement Cisco CSAF endpoint discovery/auth to unlock paginated pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-002 | Implement Cisco CSAF paginated fetch loop with dedupe and raw persistence support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | TODO Emit cosign/PGP trust metadata and advisory provenance hints for policy weighting. | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-003 | EXCITITOR-CONN-CISCO-01-003 Provider trust metadata |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.SUSE.RancherVEXHub/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors SUSE | EXCITITOR-CONN-SUSE-01-001 | Build Rancher VEX Hub discovery/subscription path with offline snapshot support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.SUSE.RancherVEXHub/TASKS.md | TODO Process hub events with resume checkpoints, deduplication, and quarantine path for malformed payloads. | Team Excititor Connectors SUSE | EXCITITOR-CONN-SUSE-01-002 | EXCITITOR-CONN-SUSE-01-002 Checkpointed event ingestion |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.SUSE.RancherVEXHub/TASKS.md | TODO Emit provider trust configuration (signers, weight overrides) and attach provenance hints for consensus engine. | Team Excititor Connectors SUSE | EXCITITOR-CONN-SUSE-01-003 | EXCITITOR-CONN-SUSE-01-003 Trust metadata & policy hints |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors MSRC | EXCITITOR-CONN-MS-01-001 | Deliver AAD onboarding/token cache for MSRC CSAF ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md | TODO Fetch CSAF packages with retry/backoff, checksum verification, and raw document persistence plus quarantine for schema failures. | Team Excititor Connectors MSRC | EXCITITOR-CONN-MS-01-002 | EXCITITOR-CONN-MS-01-002 CSAF download pipeline |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md | TODO Emit cosign/AAD issuer metadata, attach provenance details, and document policy integration. | Team Excititor Connectors MSRC | EXCITITOR-CONN-MS-01-003 | EXCITITOR-CONN-MS-01-003 Trust metadata & provenance hints |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Oracle.CSAF/TASKS.md | DOING (2025-10-17) | Team Excititor Connectors Oracle | EXCITITOR-CONN-ORACLE-01-001 | Implement Oracle CSAF catalogue discovery with CPU calendar awareness and offline snapshot import; connector wiring and fixtures underway. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Oracle.CSAF/TASKS.md | TODO Fetch CSAF documents with retry/backoff, checksum validation, revision deduplication, and raw persistence. | Team Excititor Connectors Oracle | EXCITITOR-CONN-ORACLE-01-002 | EXCITITOR-CONN-ORACLE-01-002 CSAF download & dedupe pipeline |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Oracle.CSAF/TASKS.md | TODO Emit Oracle signing metadata (PGP/cosign) and provenance hints for consensus weighting. | Team Excititor Connectors Oracle | EXCITITOR-CONN-ORACLE-01-003 | EXCITITOR-CONN-ORACLE-01-003 Trust metadata + provenance |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Ubuntu.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Ubuntu | EXCITITOR-CONN-UBUNTU-01-001 | Implement Ubuntu CSAF discovery and channel selection for USN ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Ubuntu.CSAF/TASKS.md | TODO Fetch CSAF bundles with ETag handling, checksum validation, deduplication, and raw persistence. | Team Excititor Connectors Ubuntu | EXCITITOR-CONN-UBUNTU-01-002 | EXCITITOR-CONN-UBUNTU-01-002 Incremental fetch & deduplication |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Ubuntu.CSAF/TASKS.md | TODO Emit Ubuntu signing metadata (GPG fingerprints) plus provenance hints for policy weighting and diagnostics. | Team Excititor Connectors Ubuntu | EXCITITOR-CONN-UBUNTU-01-003 | EXCITITOR-CONN-UBUNTU-01-003 Trust metadata & provenance |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-001 | Wire OCI discovery/auth to fetch OpenVEX attestations for configured images. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-002 | Attestation fetch & verify loop download DSSE attestations, trigger verification, handle retries/backoff, persist raw statements. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-003 | Provenance metadata & policy hooks emit image, subject digest, issuer, and trust metadata for policy weighting/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Cli/TASKS.md | DONE (2025-10-18) | DevEx/CLI | EXCITITOR-CLI-01-001 | Add `excititor` CLI verbs bridging to WebService with consistent auth and offline UX. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-19) | Team Excititor Core & Policy | EXCITITOR-CORE-02-001 | Context signal schema prep extend consensus models with severity/KEV/EPSS fields and update canonical serializers. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-19) | Team Excititor Policy | EXCITITOR-POLICY-02-001 | Scoring coefficients & weight ceilings add α/β options, weight boosts, and validation guidance. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-02-001 | Statement events & scoring signals create immutable VEX statement store plus consensus extensions with indexes/migrations. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.WebService/TASKS.md | TODO | Team Excititor WebService | EXCITITOR-WEB-01-004 | Resolve API & signed responses expose `/excititor/resolve`, return signed consensus/score envelopes, document auth. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-19) | Team Excititor WebService | EXCITITOR-WEB-01-005 | Mirror distribution endpoints expose download APIs for downstream Excititor instances. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-002 | Rekor v2 client integration ship transparency log client with retries and offline queue. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Worker/TASKS.md | TODO | Team Excititor Worker | EXCITITOR-WORKER-01-004 | TTL refresh & stability damper schedule re-resolve loops and guard against status flapping. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Export/TASKS.md | TODO | Team Excititor Export | EXCITITOR-EXPORT-01-005 | Score & resolve envelope surfaces include signed consensus/score artifacts in exports. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Export/TASKS.md | TODO | Team Excititor Export | EXCITITOR-EXPORT-01-006 | Quiet provenance packaging attach quieted-by statement IDs, signers, justification codes to exports and attestations. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Export/TASKS.md | TODO | Team Excititor Export | EXCITITOR-EXPORT-01-007 | Mirror bundle + domain manifest publish signed consensus bundles for mirrors. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md | TODO | Excititor Connectors Stella | EXCITITOR-CONN-STELLA-07-001 | Excititor mirror connector ingest signed mirror bundles and map to VexClaims with resume handling. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md | TODO | Excititor Connectors Stella | EXCITITOR-CONN-STELLA-07-002 | Normalize mirror bundles into VexClaim sets referencing original provider metadata and mirror provenance. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md | TODO | Excititor Connectors Stella | EXCITITOR-CONN-STELLA-07-003 | Implement incremental cursor handling per-export digest, support resume, and document configuration for downstream Excititor mirrors. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-19) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-07-001 | Advisory event log & asOf queries surface immutable statements and replay capability. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-19) | Concelier WebService Guild | FEEDWEB-EVENTS-07-001 | Advisory event replay API expose `/concelier/advisories/{key}/replay` with `asOf` filter, hex hashes, and conflict data. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | TODO | Team Core Engine & Data Science | FEEDCORE-ENGINE-07-002 | Noise prior computation service learn false-positive priors and expose deterministic summaries. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | TODO | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-07-003 | Unknown state ledger & confidence seeding persist unknown flags, seed confidence bands, expose query surface. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | TODO | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-07-001 | Advisory statement & conflict collections provision Mongo schema/indexes for event-sourced merge. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Merge/TASKS.md | TODO | BE-Merge | FEEDMERGE-ENGINE-07-001 | Conflict sets & explainers persist conflict materialization and replay hashes for merge decisions. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | TODO | Plugin Platform Guild | PLUGIN-DI-08-001 | Scoped service support in plugin bootstrap<br>Teach the plugin loader/registrar to surface services with scoped lifetimes, honour `StellaOps.DependencyInjection` metadata, and document the new contract. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | TODO | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-002 | Update Authority plugin integration<br>Flow scoped services through identity-provider registrars, bootstrap flows, and background jobs; add regression coverage around scoped lifetimes. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Normalization & Storage Backbone | FEEDSTORAGE-MONGO-08-001 | Causal-consistent Concelier storage sessions<br>Scoped session facilitator registered, repositories accept optional session handles, and replica-set failover tests verify read-your-write + monotonic reads. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Authority/TASKS.md | DONE (2025-10-19) | Authority Core & Storage Guild | AUTHSTORAGE-MONGO-08-001 | Harden Authority Mongo usage<br>Scoped Mongo sessions with majority read/write concerns wired through stores and GraphQL/HTTP pipelines; replica-set election regression validated. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | TODO | Team Excititor Storage | EXCITITOR-STORAGE-MONGO-08-001 | Causal consistency for Excititor repositories<br>Register Mongo options with majority defaults, push session-aware overloads through raw/export/consensus/cache stores, and extend migration/tests to validate causal reads after writes (including GridFS-backed content) under replica-set failover. |
| Sprint 8 | Platform Maintenance | src/StellaOps.Excititor.Worker/TASKS.md | TODO | Team Excititor Worker | EXCITITOR-WORKER-02-001 | Resolve Microsoft.Extensions.Caching.Memory advisory bump to latest .NET 10 preview, regenerate lockfiles, and rerun worker/webservice tests to clear NU1903. |
| Sprint 8 | Platform Maintenance | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | TODO | Team Excititor Storage | EXCITITOR-STORAGE-03-001 | Statement backfill tooling provide CLI/backfill scripts that populate the `vex.statements` log via WebService ingestion and validate severity/KEV/EPSS signal replay. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Exporter.Json/TASKS.md | TODO | Concelier Export Guild | CONCELIER-EXPORT-08-201 | Mirror bundle + domain manifest produce signed JSON aggregates for `*.stella-ops.org` mirrors. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-19) | Concelier Export Guild | CONCELIER-EXPORT-08-202 | Mirror-ready Trivy DB bundles mirror options emit per-domain manifests/metadata/db archives with deterministic digests for downstream sync. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.WebService/TASKS.md | TODO | Concelier WebService Guild | CONCELIER-WEB-08-201 | Mirror distribution endpoints expose domain-scoped index/download APIs with auth/quota. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | TODO | BE-Conn-Stella | FEEDCONN-STELLA-08-001 | Concelier mirror connector fetch mirror manifest, verify signatures, and hydrate canonical DTOs with resume support. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | TODO | BE-Conn-Stella | FEEDCONN-STELLA-08-002 | Map mirror payloads into canonical advisory DTOs with provenance referencing mirror domain + original source metadata. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Connector.StellaOpsMirror/TASKS.md | TODO | BE-Conn-Stella | FEEDCONN-STELLA-08-003 | Add incremental cursor + resume support (per-export fingerprint) and document configuration for downstream Concelier instances. |
| Sprint 8 | Mirror Distribution | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-MIRROR-08-001 | Managed mirror deployments for `*.stella-ops.org` Helm/Compose overlays, CDN, runbooks. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-19) | Team Scanner Core | SCANNER-CORE-09-501 | Define shared DTOs (ScanJob, ProgressEvent), error taxonomy, and deterministic ID/timestamp helpers aligning with `ARCHITECTURE_SCANNER.md` §3§4. `docs/scanner-core-contracts.md` now carries the canonical JSON snippet + acceptance notes. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-19) | Team Scanner Core | SCANNER-CORE-09-502 | Observability helpers (correlation IDs, logging scopes, metric namespacing, deterministic hashes) consumed by WebService/Worker. Added `ScannerLogExtensionsPerformanceTests` to lock ≤5µs overhead. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-503 | Security utilities: Authority client factory, OpTok caching, DPoP verifier, restart-time plug-in guardrails for scanner components. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-001 | Buildx driver scaffold + handshake with Scanner.Emit (local CAS). |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-002 | OCI annotations + provenance hand-off to Attestor. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-003 | CI demo: minimal SBOM push & backend report wiring. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-004 | Stabilize descriptor nonce derivation so repeated builds emit deterministic placeholders. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-005 | Integrate determinism guard into GitHub/Gitea workflows and archive proof artifacts. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-101 | Minimal API host with Authority enforcement, health/ready endpoints, and restart-time plug-in loader per architecture §1, §4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-102 | `/api/v1/scans` submission/status endpoints with deterministic IDs, validation, and cancellation support. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-WEB-09-103 | Progress streaming (SSE/JSONL) with correlation IDs and ISO-8601 UTC timestamps, documented in API reference. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-WEB-09-104 | Configuration binding for Mongo, MinIO, queue, feature flags; startup diagnostics and fail-fast policy. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-POLICY-09-105 | Policy snapshot loader + schema + OpenAPI (YAML ignore rules, VEX include/exclude, vendor precedence). |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-POLICY-09-106 | `/reports` verdict assembly (Feedser+Vexer+Policy) + signed response envelope. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-POLICY-09-107 | Expose score inputs, config version, and quiet provenance in `/reports` JSON and signed payload. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-201 | Worker host bootstrap with Authority auth, hosted services, and graceful shutdown semantics. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-202 | Lease/heartbeat loop with retry+jitter, poison-job quarantine, structured logging. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-203 | Analyzer dispatch skeleton emitting deterministic stage progress and honoring cancellation tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-204 | Worker metrics (queue latency, stage duration, failure counts) with OpenTelemetry resource wiring. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-205 | Harden heartbeat jitter so lease safety margin stays ≥3× and cover with regression tests + optional live queue smoke run. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-001 | Policy schema + binder + diagnostics. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-002 | Policy snapshot store + revision digests. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-003 | `/policy/preview` API (image digest → projected verdict diff). |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-CORE-09-004 | Versioned scoring config with schema validation, trust table, and golden fixtures. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-CORE-09-005 | Scoring/quiet engine compute score, enforce VEX-only quiet rules, emit inputs and provenance. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild | POLICY-CORE-09-006 | Unknown state & confidence decay deterministic bands surfaced in policy outputs. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | TODO | Policy Guild, Scanner WebService Guild | POLICY-RUNTIME-17-201 | Define runtime reachability feed contract and alignment plan for `SCANNER-RUNTIME-17-401` once Zastava endpoints land; document policy expectations for reachability tags. |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-HELM-09-001 | Helm/Compose environment profiles (dev/staging/airgap) with deterministic digests. |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | TODO | DevOps Guild, Scanner WebService Guild | DEVOPS-SCANNER-09-204 | Surface `SCANNER__EVENTS__*` environment variables across docker-compose (dev/stage/airgap) and Helm values, defaulting to share the Redis queue DSN. |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | TODO | DevOps Guild, Notify Guild | DEVOPS-SCANNER-09-205 | Add Notify smoke stage that tails the Redis stream and asserts `scanner.report.ready`/`scanner.scan.completed` reach Notify WebService in staging. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, DevEx | DOCS-ADR-09-001 | Establish ADR process and template. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, Platform Events | DOCS-EVENTS-09-002 | Publish event schema catalog (`docs/events/`) for critical envelopes. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | TODO | Platform Events Guild | PLATFORM-EVENTS-09-401 | Embed canonical event samples into contract/integration tests and ensure CI validates payloads against published schemas. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | TODO | Runtime Guild | RUNTIME-GUILD-09-402 | Confirm Scanner WebService surfaces `quietedFindingCount` and progress hints to runtime consumers; document readiness checklist. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-301 | Mongo catalog schemas/indexes for images, layers, artifacts, jobs, lifecycle rules plus migrations. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-302 | MinIO layout, immutability policies, client abstraction, and configuration binding. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-303 | Repositories/services with dual-write feature flag, deterministic digests, TTL enforcement tests. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-401 | Queue abstraction + Redis Streams adapter with ack/claim APIs and idempotency tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-402 | Pluggable backend support (Redis, NATS) with configuration binding, health probes, failover docs. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-403 | Retry + dead-letter strategy with structured logs/metrics for offline deployments. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | DONE (2025-10-19) | Scanner Cache Guild | SCANNER-CACHE-10-101 | Implement layer cache store keyed by layer digest with metadata retention per architecture §3.3. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | DONE (2025-10-19) | Scanner Cache Guild | SCANNER-CACHE-10-102 | Build file CAS with dedupe, TTL enforcement, and offline import/export hooks. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | DONE (2025-10-19) | Scanner Cache Guild | SCANNER-CACHE-10-103 | Expose cache metrics/logging and configuration toggles for warm/cold thresholds. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Cache/TASKS.md | DONE (2025-10-19) | Scanner Cache Guild | SCANNER-CACHE-10-104 | Implement cache invalidation workflows (layer delete, TTL expiry, diff invalidation). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | OS Analyzer Guild | SCANNER-ANALYZERS-OS-10-201 | Alpine/apk analyzer emitting deterministic components with provenance. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | OS Analyzer Guild | SCANNER-ANALYZERS-OS-10-202 | Debian/dpkg analyzer mapping packages to purl identity with evidence. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | OS Analyzer Guild | SCANNER-ANALYZERS-OS-10-203 | RPM analyzer capturing EVR, file listings, provenance. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | OS Analyzer Guild | SCANNER-ANALYZERS-OS-10-204 | Shared OS evidence helpers for package identity + provenance. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | OS Analyzer Guild | SCANNER-ANALYZERS-OS-10-205 | Vendor metadata enrichment (source packages, license, CVE hints). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | QA + OS Analyzer Guild | SCANNER-ANALYZERS-OS-10-206 | Determinism harness + fixtures for OS analyzers. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.OS/TASKS.md | DONE (2025-10-19) | OS Analyzer Guild + DevOps | SCANNER-ANALYZERS-OS-10-207 | Package OS analyzers as restart-time plug-ins (manifest + host registration). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-301 | Java analyzer emitting `pkg:maven` with provenance. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DOING (2025-10-19) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-302 | Node analyzer handling workspaces/symlinks emitting `pkg:npm`; workspace/symlink coverage and determinism harness in progress. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-303 | Python analyzer reading `*.dist-info`, RECORD hashes, entry points. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-304 | Go analyzer leveraging buildinfo for `pkg:golang` components. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-305 | .NET analyzer parsing `*.deps.json`, assembly metadata, RID variants. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-306 | Rust analyzer detecting crates or falling back to `bin:{sha256}`. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Shared language evidence helpers + usage flag propagation. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-308 | Determinism + fixture harness for language analyzers. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DOING (2025-10-19) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-309 | Package language analyzers as restart-time plug-ins (manifest + host registration); manifests and DI wiring under development. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/SPRINTS_LANG_IMPLEMENTATION_PLAN.md | TODO | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-302..309 | Detailed per-language sprint plan (Node, Python, Go, .NET, Rust) with gates and benchmarks. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-401 | POSIX shell AST parser with deterministic output. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-402 | Command resolution across layered rootfs with evidence attribution. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-403 | Interpreter tracing for shell wrappers to Python/Node/Java launchers. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-404 | Python entry analyzer (venv shebang, module invocation, usage flag). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-405 | Node/Java launcher analyzer capturing script/jar targets. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-406 | Explainability + diagnostics for unresolved constructs with metrics. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.EntryTrace/TASKS.md | TODO | EntryTrace Guild | SCANNER-ENTRYTRACE-10-407 | Package EntryTrace analyzers as restart-time plug-ins (manifest + host registration). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Diff/TASKS.md | TODO | Diff Guild | SCANNER-DIFF-10-501 | Build component differ tracking add/remove/version changes with deterministic ordering. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Diff/TASKS.md | TODO | Diff Guild | SCANNER-DIFF-10-502 | Attribute diffs to introducing/removing layers including provenance evidence. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Diff/TASKS.md | TODO | Diff Guild | SCANNER-DIFF-10-503 | Produce JSON diff output for inventory vs usage views aligned with API contract. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-601 | Compose inventory SBOM (CycloneDX JSON/Protobuf) from layer fragments. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-602 | Compose usage SBOM leveraging EntryTrace to flag actual usage. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-603 | Generate BOM index sidecar (purl table + roaring bitmap + usage flag). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-604 | Package artifacts for export + attestation with deterministic manifests. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-605 | Emit BOM-Index sidecar schema/fixtures (CRITICAL PATH for SP16). |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-606 | Usage view bit flags integrated with EntryTrace. |
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-607 | Embed scoring inputs, confidence band, and quiet provenance in CycloneDX/DSSE artifacts. |
| Sprint 10 | Benchmarks | bench/TASKS.md | DONE (2025-10-19) | Bench Guild, Scanner Team | BENCH-SCANNER-10-001 | Analyzer microbench harness committed with baseline CSV + CLI hook. |
| Sprint 10 | Benchmarks | bench/TASKS.md | TODO | Bench Guild, Language Analyzer Guild | BENCH-SCANNER-10-002 | Wire real language analyzers into bench harness & refresh baselines post-implementation. |
| Sprint 10 | Samples | samples/TASKS.md | DONE (2025-10-19) | Samples Guild, Scanner Team | SAMPLES-10-001 | Sample images, SBOMs, and BOM-Index fixtures published under `samples/`. |
| Sprint 10 | Samples | samples/TASKS.md | TODO | Samples Guild, Policy Guild | SAMPLES-13-004 | Add policy preview/report fixtures showing confidence bands and unknown-age tags. |
| Sprint 10 | DevOps Perf | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-PERF-10-001 | Perf smoke job added to CI enforcing <5s compose budget with regression guard. |
| Sprint 10 | DevOps Perf | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-PERF-10-002 | Publish analyzer bench metrics to Grafana/perf workbook and alarm on ≥20% regressions. |
| Sprint 10 | DevOps Perf | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-SEC-10-301 | Address NU1902/NU1903 advisories for `MongoDB.Driver` 2.12.0 and `SharpCompress` 0.23.0 surfaced during scanner cache and worker test runs. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-305A | Parse `*.deps.json` + `runtimeconfig.json`, build RID graph, and normalize to `pkg:nuget` components. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-305B | Extract assembly metadata (strong name, file/product info) and optional Authenticode details when offline cert bundle provided. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-305C | Handle self-contained apps and native assets; merge with EntryTrace usage hints. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-307D | Integrate shared helpers (license mapping, quiet provenance) and concurrency-safe caches. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-308D | Determinism fixtures + benchmark harness; compare to competitor scanners for accuracy/perf. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-309D | Package plug-in (manifest, DI registration) and update Offline Kit instructions. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-304A | Parse Go build info blob (`runtime/debug` format) and `.note.go.buildid`; map to module/version and evidence. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-304B | Implement DWARF-lite reader for VCS metadata + dirty flag; add cache to avoid re-reading identical binaries. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-304C | Fallback heuristics for stripped binaries with deterministic `bin:{sha256}` labeling and quiet provenance. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-307G | Wire shared helpers (license mapping, usage flags) and ensure concurrency-safe buffer reuse. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-308G | Determinism fixtures + benchmark harness (Vs competitor). |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-309G | Package plug-in manifest + Offline Kit notes; ensure Worker DI registration. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Node/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-302C | Surface script metadata (postinstall/preinstall) and policy hints; emit telemetry counters and evidence records. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Node/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-307N | Integrate shared helpers for license/licence evidence, canonical JSON serialization, and usage flag propagation. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Node/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-308N | Author determinism harness + fixtures for Node analyzer; add benchmark suite. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Node/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-309N | Package Node analyzer as restart-time plug-in (manifest, DI registration, Offline Kit notes). |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-303A | STREAM-based parser for `*.dist-info` (`METADATA`, `WHEEL`, `entry_points.txt`) with normalization + evidence capture. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-303B | RECORD hash verifier with chunked hashing, Zip64 support, and mismatch diagnostics. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-303C | Editable install + pip cache detection; integrate EntryTrace hints for runtime usage flags. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-307P | Shared helper integration (license metadata, quiet provenance, component merging). |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-308P | Golden fixtures + determinism harness for Python analyzer; add benchmark and hash throughput reporting. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-309P | Package plug-in (manifest, DI registration) and document Offline Kit bundling of Python stdlib metadata if needed. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-306A | Parse Cargo metadata (`Cargo.lock`, `.fingerprint`, `.metadata`) and map crates to components with evidence. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-306B | Implement heuristic classifier using ELF section names, symbol mangling, and `.comment` data for stripped binaries. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-306C | Integrate binary hash fallback (`bin:{sha256}`) and tie into shared quiet provenance helpers. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-307R | Finalize shared helper usage (license, usage flags) and concurrency-safe caches. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-308R | Determinism fixtures + performance benchmarks; compare against competitor heuristic coverage. |
| Sprint 10 | Backlog | src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md | TODO | TBD | SCANNER-ANALYZERS-LANG-10-309R | Package plug-in manifest + Offline Kit documentation; ensure Worker integration. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Authority/TASKS.md | TODO | Authority Core & Security Guild | AUTH-DPOP-11-001 | Implement DPoP proof validation + nonce handling for high-value audiences per architecture. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Authority/TASKS.md | TODO | Authority Core & Security Guild | AUTH-MTLS-11-002 | Add OAuth mTLS client credential support with certificate-bound tokens and introspection updates. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | TODO | Signer Guild | SIGNER-API-11-101 | `/sign/dsse` pipeline with Authority auth, PoE introspection, release verification, DSSE signing. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | TODO | Signer Guild | SIGNER-REF-11-102 | `/verify/referrers` endpoint with OCI lookup, caching, and policy enforcement. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Signer/TASKS.md | TODO | Signer Guild | SIGNER-QUOTA-11-103 | Enforce plan quotas, concurrency/QPS limits, artifact size caps with metrics/audit logs. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Attestor/TASKS.md | TODO | Attestor Guild | ATTESTOR-API-11-201 | `/rekor/entries` submission pipeline with dedupe, proof acquisition, and persistence. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Attestor/TASKS.md | TODO | Attestor Guild | ATTESTOR-VERIFY-11-202 | `/rekor/verify` + retrieval endpoints validating signatures and Merkle proofs. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Attestor/TASKS.md | TODO | Attestor Guild | ATTESTOR-OBS-11-203 | Telemetry, alerting, mTLS hardening, and archive workflow for Attestor. |
| Sprint 11 | UI Integration | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-ATTEST-11-005 | Attestation visibility (Rekor id, status) on Scan Detail. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Core/TASKS.md | TODO | Zastava Core Guild | ZASTAVA-CORE-12-201 | Define runtime event/admission DTOs, hashing helpers, and versioning strategy. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Core/TASKS.md | TODO | Zastava Core Guild | ZASTAVA-CORE-12-202 | Provide configuration/logging/metrics utilities shared by Observer/Webhook. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Core/TASKS.md | TODO | Zastava Core Guild | ZASTAVA-CORE-12-203 | Authority client helpers, OpTok caching, and security guardrails for runtime services. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Core/TASKS.md | TODO | Zastava Core Guild | ZASTAVA-OPS-12-204 | Operational runbooks, alert rules, and dashboard exports for runtime plane. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Observer/TASKS.md | TODO | Zastava Observer Guild | ZASTAVA-OBS-12-001 | Container lifecycle watcher emitting deterministic runtime events with buffering. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Observer/TASKS.md | TODO | Zastava Observer Guild | ZASTAVA-OBS-12-002 | Capture entrypoint traces + loaded libraries, hashing binaries and linking to baseline SBOM. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Observer/TASKS.md | TODO | Zastava Observer Guild | ZASTAVA-OBS-12-003 | Posture checks for signatures/SBOM/attestation with offline caching. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Observer/TASKS.md | TODO | Zastava Observer Guild | ZASTAVA-OBS-12-004 | Batch `/runtime/events` submissions with disk-backed buffer and rate limits. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Webhook/TASKS.md | TODO | Zastava Webhook Guild | ZASTAVA-WEBHOOK-12-101 | Admission controller host with TLS bootstrap and Authority auth. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Webhook/TASKS.md | TODO | Zastava Webhook Guild | ZASTAVA-WEBHOOK-12-102 | Query Scanner `/policy/runtime`, resolve digests, enforce verdicts. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Zastava.Webhook/TASKS.md | TODO | Zastava Webhook Guild | ZASTAVA-WEBHOOK-12-103 | Caching, fail-open/closed toggles, metrics/logging for admission decisions. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-301 | `/runtime/events` ingestion endpoint with validation, batching, storage hooks. |
| Sprint 12 | Runtime Guardrails | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-12-302 | `/policy/runtime` endpoint joining SBOM baseline + policy verdict with TTL guidance. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-AUTH-13-001 | Integrate Authority OIDC + DPoP flows with session management. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-SCANS-13-002 | Build scans module (list/detail/SBOM/diff/attestation) with performance + accessibility targets. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-VEX-13-003 | Implement VEX explorer + policy editor with preview integration. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-ADMIN-13-004 | Deliver admin area (tenants/clients/quotas/licensing) with RBAC + audit hooks. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-SCHED-13-005 | Scheduler panel: schedules CRUD, run history, dry-run preview. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | DOING (2025-10-19) | UI Guild | UI-NOTIFY-13-006 | Notify panel: channels/rules CRUD, deliveries view, test send. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.UI/TASKS.md | TODO | UI Guild | UI-POLICY-13-007 | Surface policy confidence metadata (band, age, quiet provenance) on preview and report views. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO | DevEx/CLI | CLI-RUNTIME-13-005 | Add runtime policy test verbs that consume `/policy/runtime` and display verdicts. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO | DevEx/CLI | CLI-OFFLINE-13-006 | Implement offline kit pull/import/status commands with integrity checks. |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO | DevEx/CLI | CLI-PLUGIN-13-007 | Package non-core CLI verbs as restart-time plug-ins (manifest + loader tests). |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO Once `/api/v1/scanner/policy/runtime` exits TODO, verify CLI output against final schema (field names, metadata) and update formatter/tests if the contract moves. Capture joint review notes in docs/09 and link Scanner task sign-off. | DevEx/CLI, Scanner WebService Guild | CLI-RUNTIME-13-008 | CLI-RUNTIME-13-008 Runtime policy contract sync |
| Sprint 13 | UX & CLI Experience | src/StellaOps.Cli/TASKS.md | TODO Build Spectre test harness exercising `runtime policy test` against a stubbed backend to lock output shape (table + `--json`) and guard regressions. Integrate into `dotnet test` suite. | DevEx/CLI, QA Guild | CLI-RUNTIME-13-009 | CLI-RUNTIME-13-009 Runtime policy smoke fixture |
| Sprint 14 | Release & Offline Ops | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-REL-14-001 | Deterministic build/release pipeline with SBOM/provenance, signing, and manifest generation. |
| Sprint 14 | Release & Offline Ops | ops/offline-kit/TASKS.md | TODO | Offline Kit Guild | DEVOPS-OFFLINE-14-002 | Offline kit packaging workflow with integrity verification and documentation. |
| Sprint 14 | Release & Offline Ops | ops/deployment/TASKS.md | TODO | Deployment Guild | DEVOPS-OPS-14-003 | Deployment/update/rollback automation and channel management documentation. |
| Sprint 14 | Release & Offline Ops | ops/licensing/TASKS.md | TODO | Licensing Guild | DEVOPS-LIC-14-004 | Registry token service tied to Authority, plan gating, revocation handling, monitoring. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Models/TASKS.md | TODO | Notify Models Guild | NOTIFY-MODELS-15-101 | Define core Notify DTOs, validation helpers, canonical serialization. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Models/TASKS.md | TODO | Notify Models Guild | NOTIFY-MODELS-15-102 | Publish schema docs and sample payloads for Notify. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Models/TASKS.md | TODO | Notify Models Guild | NOTIFY-MODELS-15-103 | Versioning/migration helpers for rules/templates/deliveries. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Storage.Mongo/TASKS.md | TODO | Notify Storage Guild | NOTIFY-STORAGE-15-201 | Mongo schemas/indexes for rules, channels, deliveries, digests, locks, audit. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Storage.Mongo/TASKS.md | TODO | Notify Storage Guild | NOTIFY-STORAGE-15-202 | Repositories with tenant scoping, soft delete, TTL, causal consistency options. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Storage.Mongo/TASKS.md | TODO | Notify Storage Guild | NOTIFY-STORAGE-15-203 | Delivery history retention and query APIs. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Queue/TASKS.md | TODO | Notify Queue Guild | NOTIFY-QUEUE-15-401 | Bus abstraction + Redis Streams adapter with ordering/idempotency. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Queue/TASKS.md | TODO | Notify Queue Guild | NOTIFY-QUEUE-15-402 | NATS JetStream adapter with health probes and failover. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Queue/TASKS.md | TODO | Notify Queue Guild | NOTIFY-QUEUE-15-403 | Delivery queue with retry/dead-letter + metrics. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Engine/TASKS.md | TODO | Notify Engine Guild | NOTIFY-ENGINE-15-301 | Rules evaluation core (filters, throttles, idempotency). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Engine/TASKS.md | TODO | Notify Engine Guild | NOTIFY-ENGINE-15-302 | Action planner + digest coalescer. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Engine/TASKS.md | TODO | Notify Engine Guild | NOTIFY-ENGINE-15-303 | Template rendering engine (Slack/Teams/Email/Webhook). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Engine/TASKS.md | TODO | Notify Engine Guild | NOTIFY-ENGINE-15-304 | Test-send sandbox + preview utilities. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | TODO | Notify WebService Guild | NOTIFY-WEB-15-101 | Minimal API host with Authority enforcement and plug-in loading. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | TODO | Notify WebService Guild | NOTIFY-WEB-15-102 | Rules/channel/template CRUD with audit logging. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | DONE (2025-10-19) | Notify WebService Guild | NOTIFY-WEB-15-103 | Delivery history & test-send endpoints. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | TODO | Notify WebService Guild | NOTIFY-WEB-15-104 | Configuration binding + startup diagnostics. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-201 | Bus subscription + leasing loop with backoff. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-202 | Rules evaluation pipeline integration. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-203 | Channel dispatch orchestration with retries. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Worker/TASKS.md | TODO | Notify Worker Guild | NOTIFY-WORKER-15-204 | Metrics/telemetry for Notify workers. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-501 | Slack connector with rate-limit aware delivery. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-502 | Slack health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-601 | Teams connector with Adaptive Cards. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-602 | Teams health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-701 | SMTP connector with TLS + rendering. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-702 | DKIM + health/test-send flows. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-801 | Webhook connector with signing/retries. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | DOING (2025-10-19) | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-802 | Webhook health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-503 | Package Slack connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-603 | Package Teams connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-703 | Package Email connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-EVENTS-15-201 | Emit `scanner.report.ready` + `scanner.scan.completed` events. |
| Sprint 15 | Benchmarks | bench/TASKS.md | TODO | Bench Guild, Notify Team | BENCH-NOTIFY-15-001 | Notify dispatch throughput bench with results CSV. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | TODO | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-803 | Package Webhook connector as restart-time plug-in (manifest + host registration). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | DONE (2025-10-19) | Scheduler Models Guild | SCHED-MODELS-16-101 | Define Scheduler DTOs & validation. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | DONE (2025-10-19) | Scheduler Models Guild | SCHED-MODELS-16-102 | Publish schema docs/sample payloads. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | TODO | Scheduler Models Guild | SCHED-MODELS-16-103 | Versioning/migration helpers for schedules/runs. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Storage.Mongo/TASKS.md | TODO | Scheduler Storage Guild | SCHED-STORAGE-16-201 | Mongo schemas/indexes for Scheduler state (models ready 2025-10-19). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Storage.Mongo/TASKS.md | TODO | Scheduler Storage Guild | SCHED-STORAGE-16-202 | Repositories with tenant scoping, TTL, causal consistency (models ready 2025-10-19). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Storage.Mongo/TASKS.md | TODO | Scheduler Storage Guild | SCHED-STORAGE-16-203 | Audit + stats materialization for UI (models ready 2025-10-19). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | TODO | Scheduler Queue Guild | SCHED-QUEUE-16-401 | Queue abstraction + Redis Streams adapter (samples available 2025-10-19). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | TODO | Scheduler Queue Guild | SCHED-QUEUE-16-402 | NATS JetStream adapter with health probes (samples available 2025-10-19). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | TODO | Scheduler Queue Guild | SCHED-QUEUE-16-403 | Dead-letter handling + metrics (samples available 2025-10-19). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | TODO | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-301 | Ingest BOM-Index into roaring bitmap store. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | TODO | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-302 | Query APIs for ResolveByPurls/ResolveByVulns/ResolveAll. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | TODO | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-303 | Snapshot/compaction/invalidation workflow. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | DOING | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-300 | **STUB** ImpactIndex ingest/query using fixtures (to be removed by SP16 completion). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-101 | Minimal API host with Authority enforcement. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-102 | Schedules CRUD (cron validation, pause/resume, audit). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-103 | Runs API (list/detail/cancel) + impact previews. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.WebService/TASKS.md | TODO | Scheduler WebService Guild | SCHED-WEB-16-104 | Feedser/Vexer webhook handlers with security enforcement. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Worker/TASKS.md | TODO | Scheduler Worker Guild | SCHED-WORKER-16-201 | Planner loop (cron/event triggers, leases, fairness). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Worker/TASKS.md | TODO | Scheduler Worker Guild | SCHED-WORKER-16-202 | ImpactIndex targeting and shard planning. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Worker/TASKS.md | TODO | Scheduler Worker Guild | SCHED-WORKER-16-203 | Runner execution invoking Scanner analysis/content refresh. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Worker/TASKS.md | TODO | Scheduler Worker Guild | SCHED-WORKER-16-204 | Emit rescan/report events for Notify/UI. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Worker/TASKS.md | TODO | Scheduler Worker Guild | SCHED-WORKER-16-205 | Metrics/telemetry for Scheduler planners/runners. |
| Sprint 16 | Benchmarks | bench/TASKS.md | TODO | Bench Guild, Scheduler Team | BENCH-IMPACT-16-001 | ImpactIndex throughput bench + RAM profile. |
| Sprint 17 | Symbol Intelligence & Forensics | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-17-701 | Record GNU build-id for ELF components and surface it in SBOM/diff outputs. |
| Sprint 17 | Symbol Intelligence & Forensics | src/StellaOps.Zastava.Observer/TASKS.md | TODO | Zastava Observer Guild | ZASTAVA-OBS-17-005 | Collect GNU build-id during runtime observation and attach it to emitted events. |
| Sprint 17 | Symbol Intelligence & Forensics | src/StellaOps.Scanner.WebService/TASKS.md | TODO | Scanner WebService Guild | SCANNER-RUNTIME-17-401 | Persist runtime build-id observations and expose them for debug-symbol correlation. |
| Sprint 17 | Symbol Intelligence & Forensics | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-REL-17-002 | Ship stripped debug artifacts organised by build-id within release/offline kits. |
| Sprint 17 | Symbol Intelligence & Forensics | docs/TASKS.md | TODO | Docs Guild | DOCS-RUNTIME-17-004 | Document build-id workflows for SBOMs, runtime events, and debug-store usage. |

View File

@@ -1,177 +1,208 @@
Closed sprint tasks archived from SPRINTS.md on 2025-10-19.
| Sprint | Theme | Tasks File Path | Status | Type of Specialist | Task ID | Task Description |
| --- | --- | --- | --- | --- | --- | --- |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-001 | SemVer primitive range-style metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md. This task lays the groundwork—complete the SemVer helper updates before teammates pick up FEEDMODELS-SCHEMA-01-002/003 and FEEDMODELS-SCHEMA-02-900. Use ./src/FASTER_MODELING_AND_NORMALIZATION.md for the target rule structure. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-002 | Provenance decision rationale field<br>Instructions to work:<br>AdvisoryProvenance now carries `decisionReason` and docs/tests were updated. Connectors and merge tasks should populate the field when applying precedence/freshness/tie-breaker logic; see src/StellaOps.Concelier.Models/PROVENANCE_GUIDELINES.md for usage guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-003 | Normalized version rules collection<br>Instructions to work:<br>`AffectedPackage.NormalizedVersions` and supporting comparer/docs/tests shipped. Connector owners must emit rule arrays per ./src/FASTER_MODELING_AND_NORMALIZATION.md and report progress via FEEDMERGE-COORD-02-900 so merge/storage backfills can proceed. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-02-900 | Range primitives for SemVer/EVR/NEVRA metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md before resuming this stalled effort. Confirm helpers align with the new `NormalizedVersions` representation so connectors finishing in Sprint 2 can emit consistent metadata. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>Shared `SemVerRangeRuleBuilder` now outputs primitives + normalized rules per `FASTER_MODELING_AND_NORMALIZATION.md`; CVE/GHSA connectors consuming the API have verified fixtures. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>AdvisoryStore dual-writes flattened `normalizedVersions` when `concelier.storage.enableSemVerStyle` is set; migration `20251011-semver-style-backfill` updates historical records and docs outline the rollout. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence<br>Storage now persists `provenance.decisionReason` for advisories and merge events; tests cover round-trips. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Bootstrapper seeds compound/sparse indexes for flattened normalized rules and `docs/dev/mongo_indices.md` documents query guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Updated constructors/tests keep storage suites passing with the new feature flag defaults. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Install/runbooks document connected vs air-gapped resilience profiles and monitoring hooks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Operator guides now call out `route/status/subject/clientId/scopes/bypass/remote` audit fields and SIEM triggers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and links audit signals to the rollout checklist. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.HOST | Rate limiter policy binding<br>Authority host now applies configuration-driven fixed windows to `/token`, `/authorize`, and `/internal/*`; integration tests assert 429 + `Retry-After` headers; docs/config samples refreshed for Docs guild diagrams. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.BUILD | Authority rate-limiter follow-through<br>`Security.RateLimiting` now fronts token/authorize/internal limiters; Authority + Configuration matrices (`dotnet test src/StellaOps.Authority/StellaOps.Authority.sln`, `dotnet test src/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj`) passed on 2025-10-11; awaiting #authority-core broadcast. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHCORE-BUILD-OPENIDDICT / AUTHCORE-STORAGE-DEVICE-TOKENS / AUTHCORE-BOOTSTRAP-INVITES | Address remaining Authority compile blockers (OpenIddict transaction shim, token device document, bootstrap invite cleanup) so `dotnet build src/StellaOps.Authority.sln` returns success. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | PLG6.DOC | Plugin developer guide polish<br>Section 9 now documents rate limiter metadata, config keys, and lockout interplay; YAML samples updated alongside Authority config templates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-001 | Fetch pipeline & state tracking<br>Summary planner now drives monthly/yearly VINCE fetches, persists pending summaries/notes, and hydrates VINCE detail queue with telemetry.<br>Team instructions: Read ./AGENTS.md and src/StellaOps.Concelier.Connector.CertCc/AGENTS.md. Coordinate daily with Models/Merge leads so new normalizedVersions output and provenance tags stay aligned with ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-002 | VINCE note detail fetcher<br>Summary planner queues VINCE note detail endpoints, persists raw JSON with SHA/ETag metadata, and records retry/backoff metrics. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-003 | DTO & parser implementation<br>Added VINCE DTO aggregate, Markdown→text sanitizer, vendor/status/vulnerability parsers, and parser regression fixture. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-004 | Canonical mapping & range primitives<br>VINCE DTO aggregate flows through `CertCcMapper`, emitting vendor range primitives + normalized version rules that persist via `_advisoryStore`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-005 | Deterministic fixtures/tests<br>Snapshot harness refreshed 2025-10-12; `certcc-*.snapshot.json` regenerated and regression suite green without UPDATE flag drift. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-006 | Telemetry & documentation<br>`CertCcDiagnostics` publishes summary/detail/parse/map metrics (meter `StellaOps.Concelier.Connector.CertCc`), README documents instruments, and log guidance captured for Ops on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-007 | Connector test harness remediation<br>Harness now wires `AddSourceCommon`, resets `FakeTimeProvider`, and passes canned-response regression run dated 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-008 | Snapshot coverage handoff<br>Fixtures regenerated with normalized ranges + provenance fields on 2025-10-11; QA handoff notes published and merge backfill unblocked. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-012 | Schema sync & snapshot regen follow-up<br>Fixtures regenerated with normalizedVersions + provenance decision reasons; handoff notes updated for Merge backfill 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-009 | Detail/map reintegration plan<br>Staged reintegration plan published in `src/StellaOps.Concelier.Connector.CertCc/FEEDCONN-CERTCC-02-009_PLAN.md`; coordinates enablement with FEEDCONN-CERTCC-02-004. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-010 | Partial-detail graceful degradation<br>Detail fetch now tolerates 404/403/410 responses and regression tests cover mixed endpoint availability. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Distro.RedHat/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-REDHAT-02-001 | Fixture validation sweep<br>Instructions to work:<br>Fixtures regenerated post-model-helper rollout; provenance ordering and normalizedVersions scaffolding verified via tests. Conflict resolver deltas logged in src/StellaOps.Concelier.Connector.Distro.RedHat/CONFLICT_RESOLVER_NOTES.md for Sprint 3 consumers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-001 | Canonical mapping & range primitives<br>Mapper emits SemVer rules (`scheme=apple:*`); fixtures regenerated with trimmed references + new RSR coverage, update tooling finalized. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-002 | Deterministic fixtures/tests<br>Sanitized live fixtures + regression snapshots wired into tests; normalized rule coverage asserted. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-003 | Telemetry & documentation<br>Apple meter metrics wired into Concelier WebService OpenTelemetry configuration; README and fixtures document normalizedVersions coverage. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-004 | Live HTML regression sweep<br>Sanitised HT125326/HT125328/HT106355/HT214108/HT215500 fixtures recorded and regression tests green on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-005 | Fixture regeneration tooling<br>`UPDATE_APPLE_FIXTURES=1` flow fetches & rewrites fixtures; README documents usage.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Connector.Vndr.Apple/AGENTS.md. Resume stalled tasks, ensuring normalizedVersions output and fixtures align with ./src/FASTER_MODELING_AND_NORMALIZATION.md before handing data to the conflict sprint. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance<br>Team instructions: Read ./AGENTS.md and each module's AGENTS file. Adopt the `NormalizedVersions` array emitted by the models sprint, wiring provenance `decisionReason` where merge overrides occur. Follow ./src/FASTER_MODELING_AND_NORMALIZATION.md; report via src/StellaOps.Concelier.Merge/TASKS.md (FEEDMERGE-COORD-02-900). Progress 2025-10-11: GHSA/OSV emit normalized arrays with refreshed fixtures; CVE mapper now surfaces SemVer normalized ranges; NVD/KEV adoption pending; outstanding follow-ups include FEEDSTORAGE-DATA-02-001, FEEDMERGE-ENGINE-02-002, and rolling `tools/FixtureUpdater` updates across connectors. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-CVE-02-003 | CVE normalized versions uplift |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-KEV-02-003 | KEV normalized versions propagation |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-04-003 | OSV parity fixture refresh |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-10) | Team WebService & Authority | FEEDWEB-DOCS-01-001 | Document authority toggle & scope requirements<br>Quickstart carries toggle/scope guidance pending docs guild review (no change this sprint). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Operator docs now outline connected vs air-gapped resilience profiles and monitoring cues. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Audit logging guidance highlights `route/status/subject/clientId/scopes/bypass/remote` fields and SIEM alerts. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and ties audit signals to rollout checks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | FEEDWEB-OPS-01-006 | Rename plugin drop directory to namespaced path<br>Build outputs, tests, and docs now target `StellaOps.Concelier.PluginBinaries`/`StellaOps.Authority.PluginBinaries`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | FEEDWEB-OPS-01-007 | Authority resilience adoption<br>Deployment docs and CLI notes explain the LIB5 resilience knobs for rollout.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.WebService/AGENTS.md. These items were mid-flight; resume implementation ensuring docs/operators receive timely updates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCORE-ENGINE-01-001 | CORE8.RL — Rate limiter plumbing validated; integration tests green and docs handoff recorded for middleware ordering + Retry-After headers (see `docs/dev/authority-rate-limit-tuning-outline.md` for continuing guidance). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCRYPTO-ENGINE-01-001 | SEC3.AShared metadata resolver confirmed via host test run; SEC3.B now unblocked for tuning guidance (outline captured in `docs/dev/authority-rate-limit-tuning-outline.md`). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-13) | Team Authority Platform & Security Guild | AUTHSEC-DOCS-01-002 | SEC3.B — Published `docs/security/rate-limits.md` with tuning matrix, alert thresholds, and lockout interplay guidance; Docs guild can lift copy into plugin guide. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHSEC-CRYPTO-02-001 | SEC5.B1 — Introduce libsodium signing provider and parity tests to unblock CLI verification enhancements. |
| Sprint 1 | Bootstrap & Replay Hardening | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Security Guild | AUTHSEC-CRYPTO-02-004 | SEC5.D/E — Finish bootstrap invite lifecycle (API/store/cleanup) and token device heuristics; build currently red due to pending handler integration. |
| Sprint 1 | Developer Tooling | src/StellaOps.Cli/TASKS.md | DONE (2025-10-15) | DevEx/CLI | AUTHCLI-DIAG-01-001 | Surface password policy diagnostics in CLI startup/output so operators see weakened overrides immediately.<br>CLI now loads Authority plug-ins at startup, logs weakened password policies (length/complexity), and regression coverage lives in `StellaOps.Cli.Tests/Services/AuthorityDiagnosticsReporterTests`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHPLUG-DOCS-01-001 | PLG6.DOC — Developer guide copy + diagrams merged 2025-10-11; limiter guidance incorporated and handed to Docs guild for asset export. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-12) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>`SemVerRangeRuleBuilder` shipped 2025-10-12 with comparator/`||` support and fixtures aligning to `FASTER_MODELING_AND_NORMALIZATION.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Indexes seeded + docs updated 2025-10-11 to cover flattened normalized rules for connector adoption. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDMERGE-ENGINE-02-002 | Normalized versions union & dedupe<br>Affected package resolver unions/dedupes normalized rules, stamps merge provenance with `decisionReason`, and tests cover the rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-004 | GHSA credits & ecosystem severity mapping |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-005 | GitHub quota monitoring & retries |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-006 | Production credential & scheduler rollout |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-007 | Credit parity regression fixtures |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-004 | NVD CVSS & CWE precedence payloads |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-005 | NVD merge/export parity regression |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-004 | OSV references & credits alignment |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-005 | Fixture updater workflow<br>Resolved 2025-10-12: OSV mapper now derives canonical PURLs for Go + scoped npm packages when raw payloads omit `purl`; conflict fixtures unchanged for invalid npm names. Verified via `dotnet test src/StellaOps.Concelier.Connector.Osv.Tests`, `src/StellaOps.Concelier.Connector.Ghsa.Tests`, `src/StellaOps.Concelier.Connector.Nvd.Tests`, and backbone normalization/storage suites. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Acsc/TASKS.md | DONE (2025-10-12) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ACSC-02-001 … 02-008 | Fetch→parse→map pipeline, fixtures, diagnostics, and README finished 2025-10-12; downstream export parity captured via FEEDEXPORT-JSON-04-001 / FEEDEXPORT-TRIVY-04-001 (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cccs/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CCCS-02-001 … 02-008 | Observability meter, historical harvest plan, and DOM sanitizer refinements wrapped; ops notes live under `docs/ops/concelier-cccs-operations.md` with fixtures validating EN/FR list handling. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.CertBund/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CERTBUND-02-001 … 02-008 | Telemetry/docs (02-006) and history/locale sweep (02-007) completed alongside pipeline; runbook `docs/ops/concelier-certbund-operations.md` captures locale guidance and offline packaging. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kisa/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-KISA-02-001 … 02-007 | Connector, tests, and telemetry/docs (02-006) finalized; localisation notes in `docs/dev/kisa_connector_notes.md` complete rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Bdu/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-RUBDU-02-001 … 02-008 | Fetch/parser/mapper refinements, regression fixtures, telemetry/docs, access options, and trusted root packaging all landed; README documents offline access strategy. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Nkcki/TASKS.md | DONE (2025-10-13) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-NKCKI-02-001 … 02-008 | Listing fetch, parser, mapper, fixtures, telemetry/docs, and archive plan finished; Mongo2Go/libcrypto dependency resolved via bundled OpenSSL noted in ops guide. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ICSCISA-02-001 … 02-011 | Feed parser attachment fixes, SemVer exact values, regression suites, telemetry/docs updates, and handover complete; ops runbook now details attachment verification + proxy usage. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CISCO-02-001 … 02-007 | OAuth fetch pipeline, DTO/mapping, tests, and telemetry/docs shipped; monitoring/export integration follow-ups recorded in Ops docs and exporter backlog (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Msrc/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-MSRC-02-001 … 02-008 | Azure AD onboarding (02-008) unblocked fetch/parse/map pipeline; fixtures, telemetry/docs, and Offline Kit guidance published in `docs/ops/concelier-msrc-operations.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-15) | Team Connector Support & Monitoring | FEEDCONN-CVE-02-001 … 02-002 | CVE data-source selection, fetch pipeline, and docs landed 2025-10-10. 2025-10-15: smoke verified using the seeded mirror fallback; connector now logs a warning and pulls from `seed-data/cve/` until live CVE Services credentials arrive. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Support & Monitoring | FEEDCONN-KEV-02-001 … 02-002 | KEV catalog ingestion, fixtures, telemetry, and schema validation completed 2025-10-12; ops dashboard published. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-01-001 | Canonical schema docs refresh<br>Updated canonical schema + provenance guides with SemVer style, normalized version rules, decision reason change log, and migration notes. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-001 | Concelier-SemVer Playbook<br>Published merge playbook covering mapper patterns, dedupe flow, indexes, and rollout checklist. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-002 | Normalized versions query guide<br>Delivered Mongo index/query addendum with `$unwind` recipes, dedupe checks, and operational checklist.<br>Instructions to work:<br>DONE Read ./AGENTS.md and docs/AGENTS.md. Document every schema/index/query change produced in Sprint 1-2 leveraging ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-001 | Canonical merger implementation<br>`CanonicalMerger` ships with freshness/tie-breaker logic, provenance, and unit coverage feeding Merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-002 | Field precedence and tie-breaker map<br>Field precedence tables and tie-breaker metrics wired into the canonical merge flow; docs/tests updated.<br>Instructions to work:<br>Read ./AGENTS.md and core AGENTS. Implement the conflict resolver exactly as specified in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md, coordinating with Merge and Storage teammates. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-03-001 | Merge event provenance audit prep<br>Merge events now persist `fieldDecisions` and analytics-ready provenance snapshots. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>Dual-write/backfill flag delivered; migration + options validated in tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Storage tests adjusted for normalized versions/decision reasons.<br>Instructions to work:<br>Read ./AGENTS.md and storage AGENTS. Extend merge events with decision reasons and analytics views to support the conflict rules, and deliver the dual-write/backfill for `NormalizedVersions` + `decisionReason` so connectors can roll out safely. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-001 | GHSA/NVD/OSV conflict rules<br>Merge pipeline consumes `CanonicalMerger` output prior to precedence merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-002 | Override metrics instrumentation<br>Merge events capture per-field decisions; counters/logs align with conflict rules. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-003 | Reference & credit union pipeline<br>Canonical merge preserves unions with updated tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-QA-04-001 | End-to-end conflict regression suite<br>Added regression tests (`AdvisoryMergeServiceTests`) covering canonical + precedence flow.<br>Instructions to work:<br>Read ./AGENTS.md and merge AGENTS. Integrate the canonical merger, instrument metrics, and deliver comprehensive regression tests following ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-002 | GHSA conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-NVD-04-002 | NVD conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-OSV-04-002 | OSV conflict regression fixtures<br>Instructions to work:<br>Read ./AGENTS.md and module AGENTS. Produce fixture triples supporting the precedence/tie-breaker paths defined in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md and hand them to Merge QA. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-11) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-001 | Concelier Conflict Rules<br>Runbook published at `docs/ops/concelier-conflict-resolution.md`; metrics/log guidance aligned with Sprint 3 merge counters. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-16) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-002 | Conflict runbook ops rollout<br>Ops review completed, alert thresholds applied, and change log appended in `docs/ops/concelier-conflict-resolution.md`; task closed after connector signals verified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-15) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-04-001 | Advisory schema parity (description/CWE/canonical metric)<br>Extend `Advisory` and related records with description text, CWE collection, and canonical metric pointer; refresh validation + serializer determinism tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-003 | Canonical merger parity for new fields<br>Teach `CanonicalMerger` to populate description, CWEResults, and canonical metric pointer with provenance + regression coverage. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-004 | Reference normalization & freshness instrumentation cleanup<br>Implement URL normalization for reference dedupe, align freshness-sensitive instrumentation, and add analytics tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-004 | Merge pipeline parity for new advisory fields<br>Ensure merge service + merge events surface description/CWE/canonical metric decisions with updated metrics/tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-005 | Connector coordination for new advisory fields<br>GHSA/NVD/OSV connectors now ship description, CWE, and canonical metric data with refreshed fixtures; merge coordination log updated and exporters notified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.Json/TASKS.md | DONE (2025-10-15) | Team Exporters JSON | FEEDEXPORT-JSON-04-001 | Surface new advisory fields in JSON exporter<br>Update schemas/offline bundle + fixtures once model/core parity lands.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.Json.Tests` validated canonical metric/CWE emission. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-15) | Team Exporters Trivy DB | FEEDEXPORT-TRIVY-04-001 | Propagate new advisory fields into Trivy DB package<br>Extend Bolt builder, metadata, and regression tests for the expanded schema.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.TrivyDb.Tests` confirmed canonical metric/CWE propagation. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-16) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-004 | Harden CVSS fallback so canonical metric ids persist when GitHub omits vectors; extend fixtures and document severity precedence hand-off to Merge. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-16) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-04-005 | Map OSV advisories lacking CVSS vectors to canonical metric ids/notes and document CWE provenance quirks; schedule parity fixture updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-001 | Stand up canonical VEX claim/consensus records with deterministic serializers so Storage/Exports share a stable contract. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-002 | Implement trust-weighted consensus resolver with baseline policy weights, justification gates, telemetry output, and majority/tie handling. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-003 | Publish shared connector/exporter/attestation abstractions and deterministic query signature utilities for cache/attestation workflows. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-001 | Established policy options & snapshot provider covering baseline weights/overrides. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-002 | Policy evaluator now feeds consensus resolver with immutable snapshots. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-003 | Author policy diagnostics, CLI/WebService surfacing, and documentation updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-004 | Implement YAML/JSON schema validation and deterministic diagnostics for operator bundles. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-005 | Add policy change tracking, snapshot digests, and telemetry/logging hooks. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-15) | Team Excititor Storage | EXCITITOR-STORAGE-01-001 | Mongo mapping registry plus raw/export entities and DI extensions in place. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-16) | Team Excititor Storage | EXCITITOR-STORAGE-01-004 | Build provider/consensus/cache class maps and related collections. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-15) | Team Excititor Export | EXCITITOR-EXPORT-01-001 | Export engine delivers cache lookup, manifest creation, and policy integration. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-17) | Team Excititor Export | EXCITITOR-EXPORT-01-004 | Connect export engine to attestation client and persist Rekor metadata. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-001 | Implement in-toto predicate + DSSE builder providing envelopes for export attestation. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Connectors.Abstractions/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors | EXCITITOR-CONN-ABS-01-001 | Deliver shared connector context/base classes so provider plug-ins can be activated via WebService/Worker. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-17) | Team Excititor WebService | EXCITITOR-WEB-01-001 | Scaffold minimal API host, DI, and `/excititor/status` endpoint integrating policy, storage, export, and attestation services. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Worker/TASKS.md | DONE (2025-10-17) | Team Excititor Worker | EXCITITOR-WORKER-01-001 | Create Worker host with provider scheduling and logging to drive recurring pulls/reconciliation. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CSAF-01-001 | Implement CSAF normalizer foundation translating provider documents into `VexClaim` entries. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CYCLONE-01-001 | Implement CycloneDX VEX normalizer capturing `analysis` state and component references. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-OPENVEX-01-001 | Implement OpenVEX normalizer to ingest attestations into canonical claims with provenance. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-001 | Ship Red Hat CSAF provider metadata discovery enabling incremental pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-002 | Fetch CSAF windows with ETag handling, resume tokens, quarantine on schema errors, and persist raw docs. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-003 | Populate provider trust overrides (cosign issuer, identity regex) and provenance hints for policy evaluation/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-004 | Persist resume cursors (last updated timestamp/document hashes) in storage and reload during fetch to avoid duplicates. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-005 | Register connector in Worker/WebService DI, add scheduled jobs, and document CLI triggers for Red Hat CSAF pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-006 | Add CSAF normalization parity fixtures ensuring RHSA-specific metadata is preserved. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-001 | Implement Cisco CSAF endpoint discovery/auth to unlock paginated pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-002 | Implement Cisco CSAF paginated fetch loop with dedupe and raw persistence support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.SUSE.RancherVEXHub/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors SUSE | EXCITITOR-CONN-SUSE-01-001 | Build Rancher VEX Hub discovery/subscription path with offline snapshot support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors MSRC | EXCITITOR-CONN-MS-01-001 | Deliver AAD onboarding/token cache for MSRC CSAF ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Oracle.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Oracle | EXCITITOR-CONN-ORACLE-01-001 | Implement Oracle CSAF catalogue discovery with CPU calendar awareness. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Ubuntu.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Ubuntu | EXCITITOR-CONN-UBUNTU-01-001 | Implement Ubuntu CSAF discovery and channel selection for USN ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-001 | Wire OCI discovery/auth to fetch OpenVEX attestations for configured images. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-002 | Attestation fetch & verify loop download DSSE attestations, trigger verification, handle retries/backoff, persist raw statements. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-003 | Provenance metadata & policy hooks emit image, subject digest, issuer, and trust metadata for policy weighting/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Cli/TASKS.md | DONE (2025-10-18) | DevEx/CLI | EXCITITOR-CLI-01-001 | Add `excititor` CLI verbs bridging to WebService with consistent auth and offline UX. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-19) | Team Excititor Core & Policy | EXCITITOR-CORE-02-001 | Context signal schema prep extend consensus models with severity/KEV/EPSS fields and update canonical serializers. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-19) | Team Excititor Policy | EXCITITOR-POLICY-02-001 | Scoring coefficients & weight ceilings add α/β options, weight boosts, and validation guidance. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-002 | Rekor v2 client integration ship transparency log client with retries and offline queue. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-501 | Define shared DTOs (ScanJob, ProgressEvent), error taxonomy, and deterministic ID/timestamp helpers aligning with `ARCHITECTURE_SCANNER.md` §3§4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-502 | Observability helpers (correlation IDs, logging scopes, metric namespacing, deterministic hashes) consumed by WebService/Worker. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-503 | Security utilities: Authority client factory, OpTok caching, DPoP verifier, restart-time plug-in guardrails for scanner components. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-001 | Buildx driver scaffold + handshake with Scanner.Emit (local CAS). |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-002 | OCI annotations + provenance hand-off to Attestor. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-003 | CI demo: minimal SBOM push & backend report wiring. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-004 | Stabilize descriptor nonce derivation so repeated builds emit deterministic placeholders. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-005 | Integrate determinism guard into GitHub/Gitea workflows and archive proof artifacts. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-101 | Minimal API host with Authority enforcement, health/ready endpoints, and restart-time plug-in loader per architecture §1, §4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-102 | `/api/v1/scans` submission/status endpoints with deterministic IDs, validation, and cancellation support. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-WEB-09-104 | Configuration binding for Mongo, MinIO, queue, feature flags; startup diagnostics and fail-fast policy. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-201 | Worker host bootstrap with Authority auth, hosted services, and graceful shutdown semantics. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-202 | Lease/heartbeat loop with retry+jitter, poison-job quarantine, structured logging. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-203 | Analyzer dispatch skeleton emitting deterministic stage progress and honoring cancellation tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-204 | Worker metrics (queue latency, stage duration, failure counts) with OpenTelemetry resource wiring. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-205 | Harden heartbeat jitter so lease safety margin stays ≥3× and cover with regression tests + optional live queue smoke run. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-001 | Policy schema + binder + diagnostics. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-002 | Policy snapshot store + revision digests. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-003 | `/policy/preview` API (image digest → projected verdict diff). |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-HELM-09-001 | Helm/Compose environment profiles (dev/staging/airgap) with deterministic digests. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, DevEx | DOCS-ADR-09-001 | Establish ADR process and template. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, Platform Events | DOCS-EVENTS-09-002 | Publish event schema catalog (`docs/events/`) for critical envelopes. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-301 | Mongo catalog schemas/indexes for images, layers, artifacts, jobs, lifecycle rules plus migrations. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-302 | MinIO layout, immutability policies, client abstraction, and configuration binding. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-303 | Repositories/services with dual-write feature flag, deterministic digests, TTL enforcement tests. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-401 | Queue abstraction + Redis Streams adapter with ack/claim APIs and idempotency tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-402 | Pluggable backend support (Redis, NATS) with configuration binding, health probes, failover docs. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-403 | Retry + dead-letter strategy with structured logs/metrics for offline deployments. |
Closed sprint tasks archived from SPRINTS.md on 2025-10-19.
| Sprint | Theme | Tasks File Path | Status | Type of Specialist | Task ID | Task Description |
| --- | --- | --- | --- | --- | --- | --- |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-001 | SemVer primitive range-style metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md. This task lays the groundwork—complete the SemVer helper updates before teammates pick up FEEDMODELS-SCHEMA-01-002/003 and FEEDMODELS-SCHEMA-02-900. Use ./src/FASTER_MODELING_AND_NORMALIZATION.md for the target rule structure. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-002 | Provenance decision rationale field<br>Instructions to work:<br>AdvisoryProvenance now carries `decisionReason` and docs/tests were updated. Connectors and merge tasks should populate the field when applying precedence/freshness/tie-breaker logic; see src/StellaOps.Concelier.Models/PROVENANCE_GUIDELINES.md for usage guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-11) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-01-003 | Normalized version rules collection<br>Instructions to work:<br>`AffectedPackage.NormalizedVersions` and supporting comparer/docs/tests shipped. Connector owners must emit rule arrays per ./src/FASTER_MODELING_AND_NORMALIZATION.md and report progress via FEEDMERGE-COORD-02-900 so merge/storage backfills can proceed. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-12) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-02-900 | Range primitives for SemVer/EVR/NEVRA metadata<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Models/AGENTS.md before resuming this stalled effort. Confirm helpers align with the new `NormalizedVersions` representation so connectors finishing in Sprint 2 can emit consistent metadata. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>Shared `SemVerRangeRuleBuilder` now outputs primitives + normalized rules per `FASTER_MODELING_AND_NORMALIZATION.md`; CVE/GHSA connectors consuming the API have verified fixtures. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>AdvisoryStore dual-writes flattened `normalizedVersions` when `concelier.storage.enableSemVerStyle` is set; migration `20251011-semver-style-backfill` updates historical records and docs outline the rollout. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence<br>Storage now persists `provenance.decisionReason` for advisories and merge events; tests cover round-trips. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Bootstrapper seeds compound/sparse indexes for flattened normalized rules and `docs/dev/mongo_indices.md` documents query guidance. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Updated constructors/tests keep storage suites passing with the new feature flag defaults. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-ENGINE-01-002 | Plumb Authority client resilience options<br>WebService wires `authority.resilience.*` into `AddStellaOpsAuthClient` and adds binding coverage via `AuthorityClientResilienceOptionsAreBound`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Install/runbooks document connected vs air-gapped resilience profiles and monitoring hooks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Operator guides now call out `route/status/subject/clientId/scopes/bypass/remote` audit fields and SIEM triggers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and links audit signals to the rollout checklist. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.HOST | Rate limiter policy binding<br>Authority host now applies configuration-driven fixed windows to `/token`, `/authorize`, and `/internal/*`; integration tests assert 429 + `Retry-After` headers; docs/config samples refreshed for Docs guild diagrams. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | SEC3.BUILD | Authority rate-limiter follow-through<br>`Security.RateLimiting` now fronts token/authorize/internal limiters; Authority + Configuration matrices (`dotnet test src/StellaOps.Authority/StellaOps.Authority.sln`, `dotnet test src/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj`) passed on 2025-10-11; awaiting #authority-core broadcast. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHCORE-BUILD-OPENIDDICT / AUTHCORE-STORAGE-DEVICE-TOKENS / AUTHCORE-BOOTSTRAP-INVITES | Address remaining Authority compile blockers (OpenIddict transaction shim, token device document, bootstrap invite cleanup) so `dotnet build src/StellaOps.Authority.sln` returns success. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | PLG6.DOC | Plugin developer guide polish<br>Section 9 now documents rate limiter metadata, config keys, and lockout interplay; YAML samples updated alongside Authority config templates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-001 | Fetch pipeline & state tracking<br>Summary planner now drives monthly/yearly VINCE fetches, persists pending summaries/notes, and hydrates VINCE detail queue with telemetry.<br>Team instructions: Read ./AGENTS.md and src/StellaOps.Concelier.Connector.CertCc/AGENTS.md. Coordinate daily with Models/Merge leads so new normalizedVersions output and provenance tags stay aligned with ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-002 | VINCE note detail fetcher<br>Summary planner queues VINCE note detail endpoints, persists raw JSON with SHA/ETag metadata, and records retry/backoff metrics. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-003 | DTO & parser implementation<br>Added VINCE DTO aggregate, Markdown→text sanitizer, vendor/status/vulnerability parsers, and parser regression fixture. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-004 | Canonical mapping & range primitives<br>VINCE DTO aggregate flows through `CertCcMapper`, emitting vendor range primitives + normalized version rules that persist via `_advisoryStore`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-005 | Deterministic fixtures/tests<br>Snapshot harness refreshed 2025-10-12; `certcc-*.snapshot.json` regenerated and regression suite green without UPDATE flag drift. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-006 | Telemetry & documentation<br>`CertCcDiagnostics` publishes summary/detail/parse/map metrics (meter `StellaOps.Concelier.Connector.CertCc`), README documents instruments, and log guidance captured for Ops on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-007 | Connector test harness remediation<br>Harness now wires `AddSourceCommon`, resets `FakeTimeProvider`, and passes canned-response regression run dated 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-008 | Snapshot coverage handoff<br>Fixtures regenerated with normalized ranges + provenance fields on 2025-10-11; QA handoff notes published and merge backfill unblocked. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-012 | Schema sync & snapshot regen follow-up<br>Fixtures regenerated with normalizedVersions + provenance decision reasons; handoff notes updated for Merge backfill 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-009 | Detail/map reintegration plan<br>Staged reintegration plan published in `src/StellaOps.Concelier.Connector.CertCc/FEEDCONN-CERTCC-02-009_PLAN.md`; coordinates enablement with FEEDCONN-CERTCC-02-004. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.CertCc/TASKS.md | DONE (2025-10-12) | Team Connector Resumption CERT/RedHat | FEEDCONN-CERTCC-02-010 | Partial-detail graceful degradation<br>Detail fetch now tolerates 404/403/410 responses and regression tests cover mixed endpoint availability. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Distro.RedHat/TASKS.md | DONE (2025-10-11) | Team Connector Resumption CERT/RedHat | FEEDCONN-REDHAT-02-001 | Fixture validation sweep<br>Instructions to work:<br>Fixtures regenerated post-model-helper rollout; provenance ordering and normalizedVersions scaffolding verified via tests. Conflict resolver deltas logged in src/StellaOps.Concelier.Connector.Distro.RedHat/CONFLICT_RESOLVER_NOTES.md for Sprint 3 consumers. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-001 | Canonical mapping & range primitives<br>Mapper emits SemVer rules (`scheme=apple:*`); fixtures regenerated with trimmed references + new RSR coverage, update tooling finalized. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-002 | Deterministic fixtures/tests<br>Sanitized live fixtures + regression snapshots wired into tests; normalized rule coverage asserted. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-003 | Telemetry & documentation<br>Apple meter metrics wired into Concelier WebService OpenTelemetry configuration; README and fixtures document normalizedVersions coverage. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-12) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-004 | Live HTML regression sweep<br>Sanitised HT125326/HT125328/HT106355/HT214108/HT215500 fixtures recorded and regression tests green on 2025-10-12. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md | DONE (2025-10-11) | Team Vendor Apple Specialists | FEEDCONN-APPLE-02-005 | Fixture regeneration tooling<br>`UPDATE_APPLE_FIXTURES=1` flow fetches & rewrites fixtures; README documents usage.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.Connector.Vndr.Apple/AGENTS.md. Resume stalled tasks, ensuring normalizedVersions output and fixtures align with ./src/FASTER_MODELING_AND_NORMALIZATION.md before handing data to the conflict sprint. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance<br>Team instructions: Read ./AGENTS.md and each module's AGENTS file. Adopt the `NormalizedVersions` array emitted by the models sprint, wiring provenance `decisionReason` where merge overrides occur. Follow ./src/FASTER_MODELING_AND_NORMALIZATION.md; report via src/StellaOps.Concelier.Merge/TASKS.md (FEEDMERGE-COORD-02-900). Progress 2025-10-11: GHSA/OSV emit normalized arrays with refreshed fixtures; CVE mapper now surfaces SemVer normalized ranges; NVD/KEV adoption pending; outstanding follow-ups include FEEDSTORAGE-DATA-02-001, FEEDMERGE-ENGINE-02-002, and rolling `tools/FixtureUpdater` updates across connectors. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-CVE-02-003 | CVE normalized versions uplift |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-KEV-02-003 | KEV normalized versions propagation |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-OSV-04-003 | OSV parity fixture refresh |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-10) | Team WebService & Authority | FEEDWEB-DOCS-01-001 | Document authority toggle & scope requirements<br>Quickstart carries toggle/scope guidance pending docs guild review (no change this sprint). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-003 | Author ops guidance for resilience tuning<br>Operator docs now outline connected vs air-gapped resilience profiles and monitoring cues. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-004 | Document authority bypass logging patterns<br>Audit logging guidance highlights `route/status/subject/clientId/scopes/bypass/remote` fields and SIEM alerts. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-12) | Team WebService & Authority | FEEDWEB-DOCS-01-005 | Update Concelier operator guide for enforcement cutoff<br>Install guide reiterates the 2025-12-31 cutoff and ties audit signals to rollout checks. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | FEEDWEB-OPS-01-006 | Rename plugin drop directory to namespaced path<br>Build outputs, tests, and docs now target `StellaOps.Concelier.PluginBinaries`/`StellaOps.Authority.PluginBinaries`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-11) | Team WebService & Authority | FEEDWEB-OPS-01-007 | Authority resilience adoption<br>Deployment docs and CLI notes explain the LIB5 resilience knobs for rollout.<br>Instructions to work:<br>DONE Read ./AGENTS.md and src/StellaOps.Concelier.WebService/AGENTS.md. These items were mid-flight; resume implementation ensuring docs/operators receive timely updates. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCORE-ENGINE-01-001 | CORE8.RL — Rate limiter plumbing validated; integration tests green and docs handoff recorded for middleware ordering + Retry-After headers (see `docs/dev/authority-rate-limit-tuning-outline.md` for continuing guidance). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHCRYPTO-ENGINE-01-001 | SEC3.A — Shared metadata resolver confirmed via host test run; SEC3.B now unblocked for tuning guidance (outline captured in `docs/dev/authority-rate-limit-tuning-outline.md`). |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-13) | Team Authority Platform & Security Guild | AUTHSEC-DOCS-01-002 | SEC3.BPublished `docs/security/rate-limits.md` with tuning matrix, alert thresholds, and lockout interplay guidance; Docs guild can lift copy into plugin guide. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Team Authority Platform & Security Guild | AUTHSEC-CRYPTO-02-001 | SEC5.B1Introduce libsodium signing provider and parity tests to unblock CLI verification enhancements. |
| Sprint 1 | Bootstrap & Replay Hardening | src/StellaOps.Cryptography/TASKS.md | DONE (2025-10-14) | Security Guild | AUTHSEC-CRYPTO-02-004 | SEC5.D/E — Finish bootstrap invite lifecycle (API/store/cleanup) and token device heuristics; build currently red due to pending handler integration. |
| Sprint 1 | Developer Tooling | src/StellaOps.Cli/TASKS.md | DONE (2025-10-15) | DevEx/CLI | AUTHCLI-DIAG-01-001 | Surface password policy diagnostics in CLI startup/output so operators see weakened overrides immediately.<br>CLI now loads Authority plug-ins at startup, logs weakened password policies (length/complexity), and regression coverage lives in `StellaOps.Cli.Tests/Services/AuthorityDiagnosticsReporterTests`. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/TASKS.md | DONE (2025-10-11) | Team Authority Platform & Security Guild | AUTHPLUG-DOCS-01-001 | PLG6.DOC — Developer guide copy + diagrams merged 2025-10-11; limiter guidance incorporated and handed to Docs guild for asset export. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Normalization/TASKS.md | DONE (2025-10-12) | Team Normalization & Storage Backbone | FEEDNORM-NORM-02-001 | SemVer normalized rule emitter<br>`SemVerRangeRuleBuilder` shipped 2025-10-12 with comparator/`||` support and fixtures aligning to `FASTER_MODELING_AND_NORMALIZATION.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-002 | Provenance decision reason persistence |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDSTORAGE-DATA-02-003 | Normalized versions indexing<br>Indexes seeded + docs updated 2025-10-11 to cover flattened normalized rules for connector adoption. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Normalization & Storage Backbone | FEEDMERGE-ENGINE-02-002 | Normalized versions union & dedupe<br>Affected package resolver unions/dedupes normalized rules, stamps merge provenance with `decisionReason`, and tests cover the rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-004 | GHSA credits & ecosystem severity mapping |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-005 | GitHub quota monitoring & retries |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-006 | Production credential & scheduler rollout |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-GHSA-02-007 | Credit parity regression fixtures |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-002 | NVD normalized versions & timestamps |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-004 | NVD CVSS & CWE precedence payloads |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-NVD-02-005 | NVD merge/export parity regression |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-003 | OSV normalized versions & freshness |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-11) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-004 | OSV references & credits alignment |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-02-005 | Fixture updater workflow<br>Resolved 2025-10-12: OSV mapper now derives canonical PURLs for Go + scoped npm packages when raw payloads omit `purl`; conflict fixtures unchanged for invalid npm names. Verified via `dotnet test src/StellaOps.Concelier.Connector.Osv.Tests`, `src/StellaOps.Concelier.Connector.Ghsa.Tests`, `src/StellaOps.Concelier.Connector.Nvd.Tests`, and backbone normalization/storage suites. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Acsc/TASKS.md | DONE (2025-10-12) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ACSC-02-001 … 02-008 | Fetch→parse→map pipeline, fixtures, diagnostics, and README finished 2025-10-12; downstream export parity captured via FEEDEXPORT-JSON-04-001 / FEEDEXPORT-TRIVY-04-001 (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cccs/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CCCS-02-001 … 02-008 | Observability meter, historical harvest plan, and DOM sanitizer refinements wrapped; ops notes live under `docs/ops/concelier-cccs-operations.md` with fixtures validating EN/FR list handling. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.CertBund/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CERTBUND-02-001 … 02-008 | Telemetry/docs (02-006) and history/locale sweep (02-007) completed alongside pipeline; runbook `docs/ops/concelier-certbund-operations.md` captures locale guidance and offline packaging. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kisa/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-KISA-02-001 … 02-007 | Connector, tests, and telemetry/docs (02-006) finalized; localisation notes in `docs/dev/kisa_connector_notes.md` complete rollout. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Bdu/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-RUBDU-02-001 … 02-008 | Fetch/parser/mapper refinements, regression fixtures, telemetry/docs, access options, and trusted root packaging all landed; README documents offline access strategy. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ru.Nkcki/TASKS.md | DONE (2025-10-13) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-NKCKI-02-001 … 02-008 | Listing fetch, parser, mapper, fixtures, telemetry/docs, and archive plan finished; Mongo2Go/libcrypto dependency resolved via bundled OpenSSL noted in ops guide. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md | DONE (2025-10-16) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-ICSCISA-02-001 … 02-011 | Feed parser attachment fixes, SemVer exact values, regression suites, telemetry/docs updates, and handover complete; ops runbook now details attachment verification + proxy usage. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md | DONE (2025-10-14) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-CISCO-02-001 … 02-007 | OAuth fetch pipeline, DTO/mapping, tests, and telemetry/docs shipped; monitoring/export integration follow-ups recorded in Ops docs and exporter backlog (completed). |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Vndr.Msrc/TASKS.md | DONE (2025-10-15) | Team Connector Expansion Regional & Vendor Feeds | FEEDCONN-MSRC-02-001 … 02-008 | Azure AD onboarding (02-008) unblocked fetch/parse/map pipeline; fixtures, telemetry/docs, and Offline Kit guidance published in `docs/ops/concelier-msrc-operations.md`. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Cve/TASKS.md | DONE (2025-10-15) | Team Connector Support & Monitoring | FEEDCONN-CVE-02-001 … 02-002 | CVE data-source selection, fetch pipeline, and docs landed 2025-10-10. 2025-10-15: smoke verified using the seeded mirror fallback; connector now logs a warning and pulls from `seed-data/cve/` until live CVE Services credentials arrive. |
| Sprint 2 | Connector & Data Implementation Wave | src/StellaOps.Concelier.Connector.Kev/TASKS.md | DONE (2025-10-12) | Team Connector Support & Monitoring | FEEDCONN-KEV-02-001 … 02-002 | KEV catalog ingestion, fixtures, telemetry, and schema validation completed 2025-10-12; ops dashboard published. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-01-001 | Canonical schema docs refresh<br>Updated canonical schema + provenance guides with SemVer style, normalized version rules, decision reason change log, and migration notes. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-001 | Concelier-SemVer Playbook<br>Published merge playbook covering mapper patterns, dedupe flow, indexes, and rollout checklist. |
| Sprint 2 | Connector & Data Implementation Wave | docs/TASKS.md | DONE (2025-10-11) | Team Docs & Knowledge Base | FEEDDOCS-DOCS-02-002 | Normalized versions query guide<br>Delivered Mongo index/query addendum with `$unwind` recipes, dedupe checks, and operational checklist.<br>Instructions to work:<br>DONE Read ./AGENTS.md and docs/AGENTS.md. Document every schema/index/query change produced in Sprint 1-2 leveraging ./src/FASTER_MODELING_AND_NORMALIZATION.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-001 | Canonical merger implementation<br>`CanonicalMerger` ships with freshness/tie-breaker logic, provenance, and unit coverage feeding Merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-03-002 | Field precedence and tie-breaker map<br>Field precedence tables and tie-breaker metrics wired into the canonical merge flow; docs/tests updated.<br>Instructions to work:<br>Read ./AGENTS.md and core AGENTS. Implement the conflict resolver exactly as specified in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md, coordinating with Merge and Storage teammates. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-03-001 | Merge event provenance audit prep<br>Merge events now persist `fieldDecisions` and analytics-ready provenance snapshots. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-DATA-02-001 | Normalized range dual-write + backfill<br>Dual-write/backfill flag delivered; migration + options validated in tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-11) | Team Core Engine & Storage Analytics | FEEDSTORAGE-TESTS-02-004 | Restore AdvisoryStore build after normalized versions refactor<br>Storage tests adjusted for normalized versions/decision reasons.<br>Instructions to work:<br>Read ./AGENTS.md and storage AGENTS. Extend merge events with decision reasons and analytics views to support the conflict rules, and deliver the dual-write/backfill for `NormalizedVersions` + `decisionReason` so connectors can roll out safely. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-001 | GHSA/NVD/OSV conflict rules<br>Merge pipeline consumes `CanonicalMerger` output prior to precedence merge. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-002 | Override metrics instrumentation<br>Merge events capture per-field decisions; counters/logs align with conflict rules. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-003 | Reference & credit union pipeline<br>Canonical merge preserves unions with updated tests. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-11) | Team Merge & QA Enforcement | FEEDMERGE-QA-04-001 | End-to-end conflict regression suite<br>Added regression tests (`AdvisoryMergeServiceTests`) covering canonical + precedence flow.<br>Instructions to work:<br>Read ./AGENTS.md and merge AGENTS. Integrate the canonical merger, instrument metrics, and deliver comprehensive regression tests following ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md. |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-002 | GHSA conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Nvd/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-NVD-04-002 | NVD conflict regression fixtures |
| Sprint 3 | Conflict Resolution Integration & Communications | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-12) | Team Connector Regression Fixtures | FEEDCONN-OSV-04-002 | OSV conflict regression fixtures<br>Instructions to work:<br>Read ./AGENTS.md and module AGENTS. Produce fixture triples supporting the precedence/tie-breaker paths defined in ./src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md and hand them to Merge QA. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-11) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-001 | Concelier Conflict Rules<br>Runbook published at `docs/ops/concelier-conflict-resolution.md`; metrics/log guidance aligned with Sprint 3 merge counters. |
| Sprint 3 | Conflict Resolution Integration & Communications | docs/TASKS.md | DONE (2025-10-16) | Team Documentation Guild Conflict Guidance | FEEDDOCS-DOCS-05-002 | Conflict runbook ops rollout<br>Ops review completed, alert thresholds applied, and change log appended in `docs/ops/concelier-conflict-resolution.md`; task closed after connector signals verified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Models/TASKS.md | DONE (2025-10-15) | Team Models & Merge Leads | FEEDMODELS-SCHEMA-04-001 | Advisory schema parity (description/CWE/canonical metric)<br>Extend `Advisory` and related records with description text, CWE collection, and canonical metric pointer; refresh validation + serializer determinism tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-003 | Canonical merger parity for new fields<br>Teach `CanonicalMerger` to populate description, CWEResults, and canonical metric pointer with provenance + regression coverage. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-15) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-04-004 | Reference normalization & freshness instrumentation cleanup<br>Implement URL normalization for reference dedupe, align freshness-sensitive instrumentation, and add analytics tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-004 | Merge pipeline parity for new advisory fields<br>Ensure merge service + merge events surface description/CWE/canonical metric decisions with updated metrics/tests. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-15) | Team Merge & QA Enforcement | FEEDMERGE-ENGINE-04-005 | Connector coordination for new advisory fields<br>GHSA/NVD/OSV connectors now ship description, CWE, and canonical metric data with refreshed fixtures; merge coordination log updated and exporters notified. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.Json/TASKS.md | DONE (2025-10-15) | Team Exporters JSON | FEEDEXPORT-JSON-04-001 | Surface new advisory fields in JSON exporter<br>Update schemas/offline bundle + fixtures once model/core parity lands.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.Json.Tests` validated canonical metric/CWE emission. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-15) | Team Exporters Trivy DB | FEEDEXPORT-TRIVY-04-001 | Propagate new advisory fields into Trivy DB package<br>Extend Bolt builder, metadata, and regression tests for the expanded schema.<br>2025-10-15: `dotnet test src/StellaOps.Concelier.Exporter.TrivyDb.Tests` confirmed canonical metric/CWE propagation. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-16) | Team Connector Regression Fixtures | FEEDCONN-GHSA-04-004 | Harden CVSS fallback so canonical metric ids persist when GitHub omits vectors; extend fixtures and document severity precedence hand-off to Merge. |
| Sprint 4 | Schema Parity & Freshness Alignment | src/StellaOps.Concelier.Connector.Osv/TASKS.md | DONE (2025-10-16) | Team Connector Expansion GHSA/NVD/OSV | FEEDCONN-OSV-04-005 | Map OSV advisories lacking CVSS vectors to canonical metric ids/notes and document CWE provenance quirks; schedule parity fixture updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-001 | Stand up canonical VEX claim/consensus records with deterministic serializers so Storage/Exports share a stable contract. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-002 | Implement trust-weighted consensus resolver with baseline policy weights, justification gates, telemetry output, and majority/tie handling. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-15) | Team Excititor Core & Policy | EXCITITOR-CORE-01-003 | Publish shared connector/exporter/attestation abstractions and deterministic query signature utilities for cache/attestation workflows. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-001 | Established policy options & snapshot provider covering baseline weights/overrides. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-15) | Team Excititor Policy | EXCITITOR-POLICY-01-002 | Policy evaluator now feeds consensus resolver with immutable snapshots. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-003 | Author policy diagnostics, CLI/WebService surfacing, and documentation updates. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-004 | Implement YAML/JSON schema validation and deterministic diagnostics for operator bundles. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-16) | Team Excititor Policy | EXCITITOR-POLICY-01-005 | Add policy change tracking, snapshot digests, and telemetry/logging hooks. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-15) | Team Excititor Storage | EXCITITOR-STORAGE-01-001 | Mongo mapping registry plus raw/export entities and DI extensions in place. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-16) | Team Excititor Storage | EXCITITOR-STORAGE-01-004 | Build provider/consensus/cache class maps and related collections. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-15) | Team Excititor Export | EXCITITOR-EXPORT-01-001 | Export engine delivers cache lookup, manifest creation, and policy integration. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Export/TASKS.md | DONE (2025-10-17) | Team Excititor Export | EXCITITOR-EXPORT-01-004 | Connect export engine to attestation client and persist Rekor metadata. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-001 | Implement in-toto predicate + DSSE builder providing envelopes for export attestation. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.Connectors.Abstractions/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors | EXCITITOR-CONN-ABS-01-001 | Deliver shared connector context/base classes so provider plug-ins can be activated via WebService/Worker. |
| Sprint 5 | Excititor Core Foundations | src/StellaOps.Excititor.WebService/TASKS.md | DONE (2025-10-17) | Team Excititor WebService | EXCITITOR-WEB-01-001 | Scaffold minimal API host, DI, and `/excititor/status` endpoint integrating policy, storage, export, and attestation services. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Worker/TASKS.md | DONE (2025-10-17) | Team Excititor Worker | EXCITITOR-WORKER-01-001 | Create Worker host with provider scheduling and logging to drive recurring pulls/reconciliation. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CSAF-01-001 | Implement CSAF normalizer foundation translating provider documents into `VexClaim` entries. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.CycloneDX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-CYCLONE-01-001 | Implement CycloneDX VEX normalizer capturing `analysis` state and component references. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Formats.OpenVEX/TASKS.md | DONE (2025-10-17) | Team Excititor Formats | EXCITITOR-FMT-OPENVEX-01-001 | Implement OpenVEX normalizer to ingest attestations into canonical claims with provenance. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-001 | Ship Red Hat CSAF provider metadata discovery enabling incremental pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-002 | Fetch CSAF windows with ETag handling, resume tokens, quarantine on schema errors, and persist raw docs. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-003 | Populate provider trust overrides (cosign issuer, identity regex) and provenance hints for policy evaluation/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-004 | Persist resume cursors (last updated timestamp/document hashes) in storage and reload during fetch to avoid duplicates. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-005 | Register connector in Worker/WebService DI, add scheduled jobs, and document CLI triggers for Red Hat CSAF pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.RedHat.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Red Hat | EXCITITOR-CONN-RH-01-006 | Add CSAF normalization parity fixtures ensuring RHSA-specific metadata is preserved. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-001 | Implement Cisco CSAF endpoint discovery/auth to unlock paginated pulls. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Cisco.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Cisco | EXCITITOR-CONN-CISCO-01-002 | Implement Cisco CSAF paginated fetch loop with dedupe and raw persistence support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.SUSE.RancherVEXHub/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors SUSE | EXCITITOR-CONN-SUSE-01-001 | Build Rancher VEX Hub discovery/subscription path with offline snapshot support. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors MSRC | EXCITITOR-CONN-MS-01-001 | Deliver AAD onboarding/token cache for MSRC CSAF ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Oracle.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Oracle | EXCITITOR-CONN-ORACLE-01-001 | Implement Oracle CSAF catalogue discovery with CPU calendar awareness. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.Ubuntu.CSAF/TASKS.md | DONE (2025-10-17) | Team Excititor Connectors Ubuntu | EXCITITOR-CONN-UBUNTU-01-001 | Implement Ubuntu CSAF discovery and channel selection for USN ingestion. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-001 | Wire OCI discovery/auth to fetch OpenVEX attestations for configured images. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-002 | Attestation fetch & verify loop download DSSE attestations, trigger verification, handle retries/backoff, persist raw statements. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Excititor.Connectors.OCI.OpenVEX.Attest/TASKS.md | DONE (2025-10-18) | Team Excititor Connectors OCI | EXCITITOR-CONN-OCI-01-003 | Provenance metadata & policy hooks emit image, subject digest, issuer, and trust metadata for policy weighting/logging. |
| Sprint 6 | Excititor Ingest & Formats | src/StellaOps.Cli/TASKS.md | DONE (2025-10-18) | DevEx/CLI | EXCITITOR-CLI-01-001 | Add `excititor` CLI verbs bridging to WebService with consistent auth and offline UX. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Core/TASKS.md | DONE (2025-10-19) | Team Excititor Core & Policy | EXCITITOR-CORE-02-001 | Context signal schema prep extend consensus models with severity/KEV/EPSS fields and update canonical serializers. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Policy/TASKS.md | DONE (2025-10-19) | Team Excititor Policy | EXCITITOR-POLICY-02-001 | Scoring coefficients & weight ceilings add α/β options, weight boosts, and validation guidance. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Attestation/TASKS.md | DONE (2025-10-16) | Team Excititor Attestation | EXCITITOR-ATTEST-01-002 | Rekor v2 client integration ship transparency log client with retries and offline queue. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-501 | Define shared DTOs (ScanJob, ProgressEvent), error taxonomy, and deterministic ID/timestamp helpers aligning with `ARCHITECTURE_SCANNER.md` §3§4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-502 | Observability helpers (correlation IDs, logging scopes, metric namespacing, deterministic hashes) consumed by WebService/Worker. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Core/TASKS.md | DONE (2025-10-18) | Team Scanner Core | SCANNER-CORE-09-503 | Security utilities: Authority client factory, OpTok caching, DPoP verifier, restart-time plug-in guardrails for scanner components. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-001 | Buildx driver scaffold + handshake with Scanner.Emit (local CAS). |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-002 | OCI annotations + provenance hand-off to Attestor. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-003 | CI demo: minimal SBOM push & backend report wiring. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-004 | Stabilize descriptor nonce derivation so repeated builds emit deterministic placeholders. |
| Sprint 9 | Scanner Build-time | src/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md | DONE (2025-10-19) | BuildX Guild | SP9-BLDX-09-005 | Integrate determinism guard into GitHub/Gitea workflows and archive proof artifacts. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-101 | Minimal API host with Authority enforcement, health/ready endpoints, and restart-time plug-in loader per architecture §1, §4. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-18) | Team Scanner WebService | SCANNER-WEB-09-102 | `/api/v1/scans` submission/status endpoints with deterministic IDs, validation, and cancellation support. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-19) | Team Scanner WebService | SCANNER-WEB-09-104 | Configuration binding for Mongo, MinIO, queue, feature flags; startup diagnostics and fail-fast policy. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-201 | Worker host bootstrap with Authority auth, hosted services, and graceful shutdown semantics. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-202 | Lease/heartbeat loop with retry+jitter, poison-job quarantine, structured logging. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-203 | Analyzer dispatch skeleton emitting deterministic stage progress and honoring cancellation tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-204 | Worker metrics (queue latency, stage duration, failure counts) with OpenTelemetry resource wiring. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Worker/TASKS.md | DONE (2025-10-19) | Team Scanner Worker | SCANNER-WORKER-09-205 | Harden heartbeat jitter so lease safety margin stays ≥3× and cover with regression tests + optional live queue smoke run. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-001 | Policy schema + binder + diagnostics. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-002 | Policy snapshot store + revision digests. |
| Sprint 9 | Policy Foundations | src/StellaOps.Policy/TASKS.md | DONE | Policy Guild | POLICY-CORE-09-003 | `/policy/preview` API (image digest → projected verdict diff). |
| Sprint 9 | DevOps Foundations | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-HELM-09-001 | Helm/Compose environment profiles (dev/staging/airgap) with deterministic digests. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, DevEx | DOCS-ADR-09-001 | Establish ADR process and template. |
| Sprint 9 | Docs & Governance | docs/TASKS.md | DONE (2025-10-19) | Docs Guild, Platform Events | DOCS-EVENTS-09-002 | Publish event schema catalog (`docs/events/`) for critical envelopes. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-301 | Mongo catalog schemas/indexes for images, layers, artifacts, jobs, lifecycle rules plus migrations. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-302 | MinIO layout, immutability policies, client abstraction, and configuration binding. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Storage/TASKS.md | DONE (2025-10-19) | Team Scanner Storage | SCANNER-STORAGE-09-303 | Repositories/services with dual-write feature flag, deterministic digests, TTL enforcement tests. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-401 | Queue abstraction + Redis Streams adapter with ack/claim APIs and idempotency tokens. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-402 | Pluggable backend support (Redis, NATS) with configuration binding, health probes, failover docs. |
| Sprint 9 | Scanner Core Foundations | src/StellaOps.Scanner.Queue/TASKS.md | DONE (2025-10-19) | Team Scanner Queue | SCANNER-QUEUE-09-403 | Retry + dead-letter strategy with structured logs/metrics for offline deployments. |
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.Connector.Ghsa/TASKS.md | DONE (2025-10-12) | Team Connector Normalized Versions Rollout | FEEDCONN-GHSA-02-001 | GHSA normalized versions & provenance<br>Team instructions: Read ./AGENTS.md and each module's AGENTS file. Adopt the `NormalizedVersions` array emitted by the models sprint, wiring provenance `decisionReason` where merge overrides occur. Follow ./src/FASTER_MODELING_AND_NORMALIZATION.md; report via src/StellaOps.Concelier.Merge/TASKS.md (FEEDMERGE-COORD-02-900). Progress 2025-10-11: GHSA/OSV emit normalized arrays with refreshed fixtures; CVE mapper now surfaces SemVer normalized ranges; NVD/KEV adoption pending; outstanding follow-ups include FEEDSTORAGE-DATA-02-001, FEEDMERGE-ENGINE-02-002, and rolling `tools/FixtureUpdater` updates across connectors.<br>Progress 2025-10-20: Coordination matrix + rollout dashboard refreshed; upcoming deadlines tracked (Cccs/Cisco 2025-10-21, CertBund 2025-10-22, ICS-CISA 2025-10-23, KISA 2025-10-24) with escalation path documented in FEEDMERGE-COORD-02-900.|
| Sprint 1 | Stabilize In-Progress Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-19) | Team WebService & Authority | FEEDWEB-OPS-01-006 | Rename plugin drop directory to namespaced path<br>Build outputs now point at `StellaOps.Concelier.PluginBinaries`/`StellaOps.Authority.PluginBinaries`; defaults/docs/tests updated to reflect the new layout. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-02-001 | Statement events & scoring signals immutable VEX statements store, consensus signal fields, and migration `20251019-consensus-signals-statements` with tests (`dotnet test src/StellaOps.Excititor.Core.Tests/StellaOps.Excititor.Core.Tests.csproj`, `dotnet test src/StellaOps.Excititor.Storage.Mongo.Tests/StellaOps.Excititor.Storage.Mongo.Tests.csproj`). |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Core/TASKS.md | DONE (2025-10-19) | Team Core Engine & Storage Analytics | FEEDCORE-ENGINE-07-001 | Advisory event log & asOf queries surface immutable statements and replay capability. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-19) | Concelier WebService Guild | FEEDWEB-EVENTS-07-001 | Advisory event replay API expose `/concelier/advisories/{key}/replay` with `asOf` filter, hex hashes, and conflict data. |
| Sprint 7 | Contextual Truth Foundations | src/StellaOps.Concelier.Merge/TASKS.md | DONE (2025-10-20) | BE-Merge | FEEDMERGE-ENGINE-07-001 | Conflict sets & explainers persist conflict materialization and replay hashes for merge decisions. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Concelier.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Normalization & Storage Backbone | FEEDSTORAGE-MONGO-08-001 | Causal-consistent Concelier storage sessions<br>Scoped session facilitator registered, repositories accept optional session handles, and replica-set failover tests verify read-your-write + monotonic reads. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Authority/TASKS.md | DONE (2025-10-19) | Authority Core & Storage Guild | AUTHSTORAGE-MONGO-08-001 | Harden Authority Mongo usage<br>Scoped Mongo sessions with majority read/write concerns wired through stores and GraphQL/HTTP pipelines; replica-set election regression validated. |
| Sprint 8 | Mongo strengthening | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-MONGO-08-001 | Causal consistency for Excititor repositories<br>Session-scoped repositories shipped with new Mongo records, orchestrators/workers now share scoped sessions, and replica-set failover coverage added via `dotnet test src/StellaOps.Excititor.Storage.Mongo.Tests/StellaOps.Excititor.Storage.Mongo.Tests.csproj`. |
| Sprint 8 | Platform Maintenance | src/StellaOps.Excititor.Storage.Mongo/TASKS.md | DONE (2025-10-19) | Team Excititor Storage | EXCITITOR-STORAGE-03-001 | Statement backfill tooling shipped admin backfill endpoint, CLI hook (`stellaops excititor backfill-statements`), integration tests, and operator runbook (`docs/dev/EXCITITOR_STATEMENT_BACKFILL.md`). |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Exporter.Json/TASKS.md | DONE (2025-10-19) | Concelier Export Guild | CONCELIER-EXPORT-08-201 | Mirror bundle + domain manifest produce signed JSON aggregates for `*.stella-ops.org` mirrors. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.Exporter.TrivyDb/TASKS.md | DONE (2025-10-19) | Concelier Export Guild | CONCELIER-EXPORT-08-202 | Mirror-ready Trivy DB bundles mirror options emit per-domain manifests/metadata/db archives with deterministic digests for downstream sync. |
| Sprint 8 | Mirror Distribution | src/StellaOps.Concelier.WebService/TASKS.md | DONE (2025-10-20) | Concelier WebService Guild | CONCELIER-WEB-08-201 | Mirror distribution endpoints expose domain-scoped index/download APIs with auth/quota. |
| Sprint 8 | Mirror Distribution | ops/devops/TASKS.md | DONE (2025-10-19) | DevOps Guild | DEVOPS-MIRROR-08-001 | Managed mirror deployments for `*.stella-ops.org` Helm/Compose overlays, CDN, runbooks. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | DONE (2025-10-20) | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-003 | Refactor Authority identity-provider registry to resolve scoped plugin services on-demand.<br>Introduce factory pattern aligned with scoped lifetimes decided in coordination workshop. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | DONE (2025-10-20) | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-004 | Update Authority plugin loader to activate registrars with DI support and scoped service awareness.<br>Add two-phase initialization allowing scoped dependencies post-container build. |
| Sprint 8 | Plugin Infrastructure | src/StellaOps.Plugin/TASKS.md | DONE (2025-10-20) | Plugin Platform Guild, Authority Core | PLUGIN-DI-08-005 | Provide scoped-safe bootstrap execution for Authority plugins.<br>Implement scope-per-run pattern for hosted bootstrap tasks and document migration guidance. |
| Sprint 10 | DevOps Security | ops/devops/TASKS.md | DONE (2025-10-20) | DevOps Guild | DEVOPS-SEC-10-301 | Address NU1902/NU1903 advisories for `MongoDB.Driver` 2.12.0 and `SharpCompress` 0.23.0; Wave0A prerequisites confirmed complete before remediation work. |
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Authority/TASKS.md | DONE (2025-10-20) | Authority Core & Security Guild | AUTH-DPOP-11-001 | Implement DPoP proof validation + nonce handling for high-value audiences per architecture. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.WebService/TASKS.md | DONE (2025-10-19) | Notify WebService Guild | NOTIFY-WEB-15-103 | Delivery history & test-send endpoints. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-502 | Slack health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-602 | Teams health/test-send support. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-604 | Teams health endpoint metadata alignment. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Slack/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-SLACK-15-503 | Package Slack connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Teams/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-TEAMS-15-603 | Package Teams connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Email/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-EMAIL-15-703 | Package Email connector as restart-time plug-in (manifest + host registration). |
| Sprint 15 | Notify Foundations | src/StellaOps.Scanner.WebService/TASKS.md | DONE (2025-10-20) | Scanner WebService Guild | SCANNER-EVENTS-15-201 | Emit `scanner.report.ready` + `scanner.scan.completed` events. |
| Sprint 15 | Notify Foundations | src/StellaOps.Notify.Connectors.Webhook/TASKS.md | DONE (2025-10-20) | Notify Connectors Guild | NOTIFY-CONN-WEBHOOK-15-803 | Package Webhook connector as restart-time plug-in (manifest + host registration). |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Models/TASKS.md | DONE (2025-10-20) | Scheduler Models Guild | SCHED-MODELS-16-103 | Versioning/migration helpers for schedules/runs. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | DONE (2025-10-20) | Scheduler Queue Guild | SCHED-QUEUE-16-401 | Queue abstraction + Redis Streams adapter. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.Queue/TASKS.md | DONE (2025-10-20) | Scheduler Queue Guild | SCHED-QUEUE-16-402 | NATS JetStream adapter with health probes. |
| Sprint 16 | Scheduler Intelligence | src/StellaOps.Scheduler.ImpactIndex/TASKS.md | DONE (2025-10-20) | Scheduler ImpactIndex Guild | SCHED-IMPACT-16-300 | **STUB** ImpactIndex ingest/query using fixtures (to be removed by SP16 completion). |

View File

@@ -1,2 +0,0 @@
| Sprint | Theme | Tasks File Path | Status | Type of Specialist | Task ID | Task Description |
| --- | --- | --- | --- | --- | --- | --- |

View File

@@ -2,35 +2,41 @@
The bench harness exercises the language analyzers against representative filesystem layouts so that regressions are caught before they ship.
## Layout
- `run-bench.js` Node.js script that traverses the sample `node_modules/` and `site-packages/` trees, replicating the package discovery work performed by the upcoming analyzers.
- `config.json` Declarative list of scenarios the harness executes. Each scenario points at a directory in `samples/`.
- `baseline.csv` Reference numbers captured on the 4vCPU warm rig described in `docs/12_PERFORMANCE_WORKBOOK.md`. CI publishes fresh CSVs so perf trends stay visible.
## Running locally
```bash
cd bench/Scanner.Analyzers
node run-bench.js --out baseline.csv --samples ../..
```
The harness prints a table to stdout and writes the CSV (if `--out` is specified) with the following headers:
```
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
```
Use `--iterations` to override the default (5 passes per scenario) and `--threshold-ms` to customize the failure budget. Budgets default to 5000ms, aligned with the SBOM compose objective.
## Adding scenarios
1. Drop the fixture tree under `samples/<area>/...`.
2. Append a new scenario entry to `config.json` describing:
- `id` snake_case scenario name (also used in CSV).
- `label` human-friendly description shown in logs.
- `root` path to the directory that will be scanned.
- `matcher` glob describing files that will be parsed (POSIX `**` patterns).
- `parser` `node` or `python` to choose the metadata reader.
3. Re-run `node run-bench.js --out baseline.csv`.
4. Commit both the fixture and updated baseline.
The harness is intentionally dependency-free to remain runnable inside minimal CI runners.
## Layout
- `StellaOps.Bench.ScannerAnalyzers/` .NET 10 console harness that executes the real language analyzers (and fallback metadata walks for ecosystems that are still underway).
- `config.json` Declarative list of scenarios the harness executes. Each scenario points at a directory in `samples/`.
- `baseline.csv` Reference numbers captured on the 4vCPU warm rig described in `docs/12_PERFORMANCE_WORKBOOK.md`. CI publishes fresh CSVs so perf trends stay visible.
## Current scenarios
- `node_monorepo_walk` → runs the Node analyzer across `samples/runtime/npm-monorepo`.
- `java_demo_archive` → runs the Java analyzer against `samples/runtime/java-demo/libs/demo.jar`.
- `python_site_packages_walk` → temporary metadata walk over `samples/runtime/python-venv` until the Python analyzer lands.
## Running locally
```bash
dotnet run \
--project bench/Scanner.Analyzers/StellaOps.Bench.ScannerAnalyzers/StellaOps.Bench.ScannerAnalyzers.csproj \
-- \
--repo-root . \
--out bench/Scanner.Analyzers/baseline.csv
```
The harness prints a table to stdout and writes the CSV (if `--out` is specified) with the following headers:
```
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
```
Use `--iterations` to override the default (5 passes per scenario) and `--threshold-ms` to customize the failure budget. Budgets default to 5000ms (or per-scenario overrides in `config.json`), aligned with the SBOM compose objective.
## Adding scenarios
1. Drop the fixture tree under `samples/<area>/...`.
2. Append a new scenario entry to `config.json` describing:
- `id` snake_case scenario name (also used in CSV).
- `label` human-friendly description shown in logs.
- `root` path to the directory that will be scanned.
- For analyzer-backed scenarios, set `analyzers` to the list of language analyzer ids (for example, `["node"]`).
- For temporary metadata walks (used until the analyzer ships), provide `parser` (`node` or `python`) and the `matcher` glob describing files to parse.
3. Re-run the harness (`dotnet run … --out baseline.csv`).
4. Commit both the fixture and updated baseline.

View File

@@ -0,0 +1,104 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StellaOps.Bench.ScannerAnalyzers;
internal sealed record BenchmarkConfig
{
[JsonPropertyName("iterations")]
public int? Iterations { get; init; }
[JsonPropertyName("thresholdMs")]
public double? ThresholdMs { get; init; }
[JsonPropertyName("scenarios")]
public List<BenchmarkScenarioConfig> Scenarios { get; init; } = new();
public static async Task<BenchmarkConfig> LoadAsync(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Config path is required.", nameof(path));
}
await using var stream = File.OpenRead(path);
var config = await JsonSerializer.DeserializeAsync<BenchmarkConfig>(stream, SerializerOptions).ConfigureAwait(false);
if (config is null)
{
throw new InvalidOperationException($"Failed to parse benchmark config '{path}'.");
}
if (config.Scenarios.Count == 0)
{
throw new InvalidOperationException("config.scenarios must declare at least one scenario.");
}
foreach (var scenario in config.Scenarios)
{
scenario.Validate();
}
return config;
}
private static JsonSerializerOptions SerializerOptions => new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
}
internal sealed record BenchmarkScenarioConfig
{
[JsonPropertyName("id")]
public string? Id { get; init; }
[JsonPropertyName("label")]
public string? Label { get; init; }
[JsonPropertyName("root")]
public string? Root { get; init; }
[JsonPropertyName("analyzers")]
public List<string>? Analyzers { get; init; }
[JsonPropertyName("matcher")]
public string? Matcher { get; init; }
[JsonPropertyName("parser")]
public string? Parser { get; init; }
[JsonPropertyName("thresholdMs")]
public double? ThresholdMs { get; init; }
public bool HasAnalyzers => Analyzers is { Count: > 0 };
public void Validate()
{
if (string.IsNullOrWhiteSpace(Id))
{
throw new InvalidOperationException("scenario.id is required.");
}
if (string.IsNullOrWhiteSpace(Root))
{
throw new InvalidOperationException($"Scenario '{Id}' must specify a root path.");
}
if (HasAnalyzers)
{
return;
}
if (string.IsNullOrWhiteSpace(Parser))
{
throw new InvalidOperationException($"Scenario '{Id}' must specify parser or analyzers.");
}
if (string.IsNullOrWhiteSpace(Matcher))
{
throw new InvalidOperationException($"Scenario '{Id}' must specify matcher when parser is used.");
}
}
}

View File

@@ -0,0 +1,302 @@
using System.Globalization;
using StellaOps.Bench.ScannerAnalyzers.Scenarios;
namespace StellaOps.Bench.ScannerAnalyzers;
internal static class Program
{
public static async Task<int> Main(string[] args)
{
try
{
var options = ProgramOptions.Parse(args);
var config = await BenchmarkConfig.LoadAsync(options.ConfigPath).ConfigureAwait(false);
var iterations = options.Iterations ?? config.Iterations ?? 5;
var thresholdMs = options.ThresholdMs ?? config.ThresholdMs ?? 5000;
var repoRoot = ResolveRepoRoot(options.RepoRoot, options.ConfigPath);
var results = new List<ScenarioResult>();
var failures = new List<string>();
foreach (var scenario in config.Scenarios)
{
var runner = ScenarioRunnerFactory.Create(scenario);
var scenarioRoot = ResolveScenarioRoot(repoRoot, scenario.Root!);
var execution = await runner.ExecuteAsync(scenarioRoot, iterations, CancellationToken.None).ConfigureAwait(false);
var stats = ScenarioStatistics.FromDurations(execution.Durations);
var scenarioThreshold = scenario.ThresholdMs ?? thresholdMs;
results.Add(new ScenarioResult(
scenario.Id!,
scenario.Label ?? scenario.Id!,
execution.SampleCount,
stats.MeanMs,
stats.P95Ms,
stats.MaxMs,
iterations));
if (stats.MaxMs > scenarioThreshold)
{
failures.Add($"{scenario.Id} exceeded threshold: {stats.MaxMs:F2} ms > {scenarioThreshold:F2} ms");
}
}
TablePrinter.Print(results);
if (!string.IsNullOrWhiteSpace(options.OutPath))
{
CsvWriter.Write(options.OutPath!, results);
}
if (failures.Count > 0)
{
Console.Error.WriteLine();
Console.Error.WriteLine("Performance threshold exceeded:");
foreach (var failure in failures)
{
Console.Error.WriteLine($" - {failure}");
}
return 1;
}
return 0;
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
return 1;
}
}
private static string ResolveRepoRoot(string? overridePath, string configPath)
{
if (!string.IsNullOrWhiteSpace(overridePath))
{
return Path.GetFullPath(overridePath);
}
var configDirectory = Path.GetDirectoryName(configPath);
if (string.IsNullOrWhiteSpace(configDirectory))
{
return Directory.GetCurrentDirectory();
}
return Path.GetFullPath(Path.Combine(configDirectory, "..", ".."));
}
private static string ResolveScenarioRoot(string repoRoot, string relativeRoot)
{
if (string.IsNullOrWhiteSpace(relativeRoot))
{
throw new InvalidOperationException("Scenario root is required.");
}
var combined = Path.GetFullPath(Path.Combine(repoRoot, relativeRoot));
if (!PathUtilities.IsWithinRoot(repoRoot, combined))
{
throw new InvalidOperationException($"Scenario root '{relativeRoot}' escapes repository root '{repoRoot}'.");
}
if (!Directory.Exists(combined))
{
throw new DirectoryNotFoundException($"Scenario root '{combined}' does not exist.");
}
return combined;
}
private sealed record ProgramOptions(string ConfigPath, int? Iterations, double? ThresholdMs, string? OutPath, string? RepoRoot)
{
public static ProgramOptions Parse(string[] args)
{
var configPath = DefaultConfigPath();
int? iterations = null;
double? thresholdMs = null;
string? outPath = null;
string? repoRoot = null;
for (var index = 0; index < args.Length; index++)
{
var current = args[index];
switch (current)
{
case "--config":
EnsureNext(args, index);
configPath = Path.GetFullPath(args[++index]);
break;
case "--iterations":
EnsureNext(args, index);
iterations = int.Parse(args[++index], CultureInfo.InvariantCulture);
break;
case "--threshold-ms":
EnsureNext(args, index);
thresholdMs = double.Parse(args[++index], CultureInfo.InvariantCulture);
break;
case "--out":
EnsureNext(args, index);
outPath = args[++index];
break;
case "--repo-root":
case "--samples":
EnsureNext(args, index);
repoRoot = args[++index];
break;
default:
throw new ArgumentException($"Unknown argument: {current}", nameof(args));
}
}
return new ProgramOptions(configPath, iterations, thresholdMs, outPath, repoRoot);
}
private static string DefaultConfigPath()
{
var binaryDir = AppContext.BaseDirectory;
var projectRoot = Path.GetFullPath(Path.Combine(binaryDir, "..", "..", ".."));
var configDirectory = Path.GetFullPath(Path.Combine(projectRoot, ".."));
return Path.Combine(configDirectory, "config.json");
}
private static void EnsureNext(string[] args, int index)
{
if (index + 1 >= args.Length)
{
throw new ArgumentException("Missing value for argument.", nameof(args));
}
}
}
private sealed record ScenarioResult(
string Id,
string Label,
int SampleCount,
double MeanMs,
double P95Ms,
double MaxMs,
int Iterations);
private sealed record ScenarioStatistics(double MeanMs, double P95Ms, double MaxMs)
{
public static ScenarioStatistics FromDurations(IReadOnlyList<double> durations)
{
if (durations.Count == 0)
{
return new ScenarioStatistics(0, 0, 0);
}
var sorted = durations.ToArray();
Array.Sort(sorted);
var total = 0d;
foreach (var value in durations)
{
total += value;
}
var mean = total / durations.Count;
var p95 = Percentile(sorted, 95);
var max = sorted[^1];
return new ScenarioStatistics(mean, p95, max);
}
private static double Percentile(IReadOnlyList<double> sorted, double percentile)
{
if (sorted.Count == 0)
{
return 0;
}
var rank = (percentile / 100d) * (sorted.Count - 1);
var lower = (int)Math.Floor(rank);
var upper = (int)Math.Ceiling(rank);
var weight = rank - lower;
if (upper >= sorted.Count)
{
return sorted[lower];
}
return sorted[lower] + weight * (sorted[upper] - sorted[lower]);
}
}
private static class TablePrinter
{
public static void Print(IEnumerable<ScenarioResult> results)
{
Console.WriteLine("Scenario | Count | Mean(ms) | P95(ms) | Max(ms)");
Console.WriteLine("---------------------------- | ----- | --------- | --------- | ----------");
foreach (var row in results)
{
Console.WriteLine(FormatRow(row));
}
}
private static string FormatRow(ScenarioResult row)
{
var idColumn = row.Id.Length <= 28
? row.Id.PadRight(28)
: row.Id[..28];
return string.Join(" | ", new[]
{
idColumn,
row.SampleCount.ToString(CultureInfo.InvariantCulture).PadLeft(5),
row.MeanMs.ToString("F2", CultureInfo.InvariantCulture).PadLeft(9),
row.P95Ms.ToString("F2", CultureInfo.InvariantCulture).PadLeft(9),
row.MaxMs.ToString("F2", CultureInfo.InvariantCulture).PadLeft(10),
});
}
}
private static class CsvWriter
{
public static void Write(string path, IEnumerable<ScenarioResult> results)
{
var resolvedPath = Path.GetFullPath(path);
var directory = Path.GetDirectoryName(resolvedPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
using var stream = new FileStream(resolvedPath, FileMode.Create, FileAccess.Write, FileShare.None);
using var writer = new StreamWriter(stream);
writer.WriteLine("scenario,iterations,sample_count,mean_ms,p95_ms,max_ms");
foreach (var row in results)
{
writer.Write(row.Id);
writer.Write(',');
writer.Write(row.Iterations.ToString(CultureInfo.InvariantCulture));
writer.Write(',');
writer.Write(row.SampleCount.ToString(CultureInfo.InvariantCulture));
writer.Write(',');
writer.Write(row.MeanMs.ToString("F4", CultureInfo.InvariantCulture));
writer.Write(',');
writer.Write(row.P95Ms.ToString("F4", CultureInfo.InvariantCulture));
writer.Write(',');
writer.Write(row.MaxMs.ToString("F4", CultureInfo.InvariantCulture));
writer.WriteLine();
}
}
}
internal static class PathUtilities
{
public static bool IsWithinRoot(string root, string candidate)
{
var relative = Path.GetRelativePath(root, candidate);
if (string.IsNullOrEmpty(relative) || relative == ".")
{
return true;
}
return !relative.StartsWith("..", StringComparison.Ordinal) && !Path.IsPathRooted(relative);
}
}
}

View File

@@ -0,0 +1,279 @@
using System.Diagnostics;
using System.Text;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using StellaOps.Scanner.Analyzers.Lang;
using StellaOps.Scanner.Analyzers.Lang.Java;
using StellaOps.Scanner.Analyzers.Lang.Node;
namespace StellaOps.Bench.ScannerAnalyzers.Scenarios;
internal interface IScenarioRunner
{
Task<ScenarioExecutionResult> ExecuteAsync(string rootPath, int iterations, CancellationToken cancellationToken);
}
internal sealed record ScenarioExecutionResult(double[] Durations, int SampleCount);
internal static class ScenarioRunnerFactory
{
public static IScenarioRunner Create(BenchmarkScenarioConfig scenario)
{
if (scenario.HasAnalyzers)
{
return new LanguageAnalyzerScenarioRunner(scenario.Analyzers!);
}
if (string.IsNullOrWhiteSpace(scenario.Parser) || string.IsNullOrWhiteSpace(scenario.Matcher))
{
throw new InvalidOperationException($"Scenario '{scenario.Id}' missing parser or matcher configuration.");
}
return new MetadataWalkScenarioRunner(scenario.Parser, scenario.Matcher);
}
}
internal sealed class LanguageAnalyzerScenarioRunner : IScenarioRunner
{
private readonly IReadOnlyList<Func<ILanguageAnalyzer>> _analyzerFactories;
public LanguageAnalyzerScenarioRunner(IEnumerable<string> analyzerIds)
{
if (analyzerIds is null)
{
throw new ArgumentNullException(nameof(analyzerIds));
}
_analyzerFactories = analyzerIds
.Where(static id => !string.IsNullOrWhiteSpace(id))
.Select(CreateFactory)
.ToArray();
if (_analyzerFactories.Count == 0)
{
throw new InvalidOperationException("At least one analyzer id must be provided.");
}
}
public async Task<ScenarioExecutionResult> ExecuteAsync(string rootPath, int iterations, CancellationToken cancellationToken)
{
if (iterations <= 0)
{
throw new ArgumentOutOfRangeException(nameof(iterations), iterations, "Iterations must be positive.");
}
var analyzers = _analyzerFactories.Select(factory => factory()).ToArray();
var engine = new LanguageAnalyzerEngine(analyzers);
var durations = new double[iterations];
var componentCount = -1;
for (var i = 0; i < iterations; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var context = new LanguageAnalyzerContext(rootPath, TimeProvider.System);
var stopwatch = Stopwatch.StartNew();
var result = await engine.AnalyzeAsync(context, cancellationToken).ConfigureAwait(false);
stopwatch.Stop();
durations[i] = stopwatch.Elapsed.TotalMilliseconds;
var currentCount = result.Components.Count;
if (componentCount < 0)
{
componentCount = currentCount;
}
else if (componentCount != currentCount)
{
throw new InvalidOperationException($"Analyzer output count changed between iterations ({componentCount} vs {currentCount}).");
}
}
if (componentCount < 0)
{
componentCount = 0;
}
return new ScenarioExecutionResult(durations, componentCount);
}
private static Func<ILanguageAnalyzer> CreateFactory(string analyzerId)
{
var id = analyzerId.Trim().ToLowerInvariant();
return id switch
{
"java" => static () => new JavaLanguageAnalyzer(),
"node" => static () => new NodeLanguageAnalyzer(),
_ => throw new InvalidOperationException($"Unsupported analyzer '{analyzerId}'."),
};
}
}
internal sealed class MetadataWalkScenarioRunner : IScenarioRunner
{
private readonly Regex _matcher;
private readonly string _parserKind;
public MetadataWalkScenarioRunner(string parserKind, string globPattern)
{
_parserKind = parserKind?.Trim().ToLowerInvariant() ?? throw new ArgumentNullException(nameof(parserKind));
_matcher = GlobToRegex(globPattern ?? throw new ArgumentNullException(nameof(globPattern)));
}
public async Task<ScenarioExecutionResult> ExecuteAsync(string rootPath, int iterations, CancellationToken cancellationToken)
{
if (iterations <= 0)
{
throw new ArgumentOutOfRangeException(nameof(iterations), iterations, "Iterations must be positive.");
}
var durations = new double[iterations];
var sampleCount = -1;
for (var i = 0; i < iterations; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var stopwatch = Stopwatch.StartNew();
var files = EnumerateMatchingFiles(rootPath);
if (files.Count == 0)
{
throw new InvalidOperationException($"Parser '{_parserKind}' matched zero files under '{rootPath}'.");
}
foreach (var file in files)
{
cancellationToken.ThrowIfCancellationRequested();
await ParseAsync(file).ConfigureAwait(false);
}
stopwatch.Stop();
durations[i] = stopwatch.Elapsed.TotalMilliseconds;
if (sampleCount < 0)
{
sampleCount = files.Count;
}
else if (sampleCount != files.Count)
{
throw new InvalidOperationException($"File count changed between iterations ({sampleCount} vs {files.Count}).");
}
}
if (sampleCount < 0)
{
sampleCount = 0;
}
return new ScenarioExecutionResult(durations, sampleCount);
}
private async ValueTask ParseAsync(string filePath)
{
switch (_parserKind)
{
case "node":
{
using var stream = File.OpenRead(filePath);
using var document = await JsonDocument.ParseAsync(stream).ConfigureAwait(false);
if (!document.RootElement.TryGetProperty("name", out var name) || name.ValueKind != JsonValueKind.String)
{
throw new InvalidOperationException($"package.json '{filePath}' missing name.");
}
if (!document.RootElement.TryGetProperty("version", out var version) || version.ValueKind != JsonValueKind.String)
{
throw new InvalidOperationException($"package.json '{filePath}' missing version.");
}
}
break;
case "python":
{
var (name, version) = await ParsePythonMetadataAsync(filePath).ConfigureAwait(false);
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(version))
{
throw new InvalidOperationException($"METADATA '{filePath}' missing Name/Version.");
}
}
break;
default:
throw new InvalidOperationException($"Unknown parser '{_parserKind}'.");
}
}
private static async Task<(string? Name, string? Version)> ParsePythonMetadataAsync(string filePath)
{
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
using var reader = new StreamReader(stream);
string? name = null;
string? version = null;
while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line)
{
if (line.StartsWith("Name:", StringComparison.OrdinalIgnoreCase))
{
name ??= line[5..].Trim();
}
else if (line.StartsWith("Version:", StringComparison.OrdinalIgnoreCase))
{
version ??= line[8..].Trim();
}
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(version))
{
break;
}
}
return (name, version);
}
private IReadOnlyList<string> EnumerateMatchingFiles(string rootPath)
{
var files = new List<string>();
var stack = new Stack<string>();
stack.Push(rootPath);
while (stack.Count > 0)
{
var current = stack.Pop();
foreach (var directory in Directory.EnumerateDirectories(current))
{
stack.Push(directory);
}
foreach (var file in Directory.EnumerateFiles(current))
{
var relative = Path.GetRelativePath(rootPath, file).Replace('\\', '/');
if (_matcher.IsMatch(relative))
{
files.Add(file);
}
}
}
return files;
}
private static Regex GlobToRegex(string pattern)
{
if (string.IsNullOrWhiteSpace(pattern))
{
throw new ArgumentException("Glob pattern is required.", nameof(pattern));
}
var normalized = pattern.Replace("\\", "/");
normalized = normalized.Replace("**", "\u0001");
normalized = normalized.Replace("*", "\u0002");
var escaped = Regex.Escape(normalized);
escaped = escaped.Replace("\u0001/", "(?:.*/)?", StringComparison.Ordinal);
escaped = escaped.Replace("\u0001", ".*", StringComparison.Ordinal);
escaped = escaped.Replace("\u0002", "[^/]*", StringComparison.Ordinal);
return new Regex("^" + escaped + "$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang\StellaOps.Scanner.Analyzers.Lang.csproj" />
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.Node\StellaOps.Scanner.Analyzers.Lang.Node.csproj" />
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.Java\StellaOps.Scanner.Analyzers.Lang.Java.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,3 +1,4 @@
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
node_monorepo_walk,5,4,233.9428,319.8564,344.4611
python_site_packages_walk,5,3,72.9166,74.8970,74.9884
node_monorepo_walk,5,4,4.2314,15.3277,18.9984
java_demo_archive,5,1,4.5572,17.3489,21.5472
python_site_packages_walk,5,3,2.0049,6.4230,7.8832
1 scenario iterations sample_count mean_ms p95_ms max_ms
2 node_monorepo_walk 5 4 233.9428 4.2314 319.8564 15.3277 344.4611 18.9984
3 python_site_packages_walk java_demo_archive 5 3 1 72.9166 4.5572 74.8970 17.3489 74.9884 21.5472
4 python_site_packages_walk 5 3 2.0049 6.4230 7.8832

View File

@@ -2,17 +2,26 @@
"thresholdMs": 5000,
"iterations": 5,
"scenarios": [
{
"id": "node_monorepo_walk",
"label": "Node.js monorepo package.json harvest",
"root": "samples/runtime/npm-monorepo/node_modules",
"matcher": "**/package.json",
"parser": "node"
},
{
"id": "python_site_packages_walk",
"label": "Python site-packages dist-info crawl",
"root": "samples/runtime/python-venv/lib/python3.11/site-packages",
{
"id": "node_monorepo_walk",
"label": "Node.js analyzer on monorepo fixture",
"root": "samples/runtime/npm-monorepo",
"analyzers": [
"node"
]
},
{
"id": "java_demo_archive",
"label": "Java analyzer on demo jar",
"root": "samples/runtime/java-demo",
"analyzers": [
"java"
]
},
{
"id": "python_site_packages_walk",
"label": "Python site-packages dist-info crawl",
"root": "samples/runtime/python-venv/lib/python3.11/site-packages",
"matcher": "**/*.dist-info/METADATA",
"parser": "python"
}

View File

@@ -1,249 +0,0 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const path = require('path');
const { performance } = require('perf_hooks');
function globToRegExp(pattern) {
let working = pattern
.replace(/\*\*/g, ':::DOUBLE_WILDCARD:::')
.replace(/\*/g, ':::SINGLE_WILDCARD:::');
working = working.replace(/([.+^${}()|[\]\\])/g, '\\$1');
working = working
.replace(/:::DOUBLE_WILDCARD:::\//g, '(?:.*/)?')
.replace(/:::DOUBLE_WILDCARD:::/g, '.*')
.replace(/:::SINGLE_WILDCARD:::/g, '[^/]*');
return new RegExp(`^${working}$`);
}
function walkFiles(root, matcher) {
const out = [];
const stack = [root];
while (stack.length) {
const current = stack.pop();
const stat = fs.statSync(current, { throwIfNoEntry: true });
if (stat.isDirectory()) {
const entries = fs.readdirSync(current);
for (const entry of entries) {
stack.push(path.join(current, entry));
}
} else if (stat.isFile()) {
const relativePath = path.relative(root, current).replace(/\\/g, '/');
if (matcher.test(relativePath)) {
out.push(current);
}
}
}
return out;
}
function parseArgs(argv) {
const args = {
config: path.join(__dirname, 'config.json'),
iterations: undefined,
thresholdMs: undefined,
out: undefined,
repoRoot: path.join(__dirname, '..', '..'),
};
for (let i = 2; i < argv.length; i++) {
const current = argv[i];
switch (current) {
case '--config':
args.config = argv[++i];
break;
case '--iterations':
args.iterations = Number(argv[++i]);
break;
case '--threshold-ms':
args.thresholdMs = Number(argv[++i]);
break;
case '--out':
args.out = argv[++i];
break;
case '--repo-root':
case '--samples':
args.repoRoot = argv[++i];
break;
default:
throw new Error(`Unknown argument: ${current}`);
}
}
return args;
}
function loadConfig(configPath) {
const json = fs.readFileSync(configPath, 'utf8');
const cfg = JSON.parse(json);
if (!Array.isArray(cfg.scenarios) || cfg.scenarios.length === 0) {
throw new Error('config.scenarios must be a non-empty array');
}
return cfg;
}
function ensureWithinRepo(repoRoot, target) {
const relative = path.relative(repoRoot, target);
if (relative === '' || relative === '.') {
return true;
}
return !relative.startsWith('..') && !path.isAbsolute(relative);
}
function parseNodePackage(contents) {
const parsed = JSON.parse(contents);
if (!parsed.name || !parsed.version) {
throw new Error('package.json missing name/version');
}
return { name: parsed.name, version: parsed.version };
}
function parsePythonMetadata(contents) {
let name;
let version;
for (const line of contents.split(/\r?\n/)) {
if (!name && line.startsWith('Name:')) {
name = line.slice(5).trim();
} else if (!version && line.startsWith('Version:')) {
version = line.slice(8).trim();
}
if (name && version) {
break;
}
}
if (!name || !version) {
throw new Error('METADATA missing Name/Version headers');
}
return { name, version };
}
function formatRow(row) {
const cols = [
row.id.padEnd(28),
row.sampleCount.toString().padStart(5),
row.meanMs.toFixed(2).padStart(9),
row.p95Ms.toFixed(2).padStart(9),
row.maxMs.toFixed(2).padStart(9),
];
return cols.join(' | ');
}
function percentile(sortedDurations, percentile) {
if (sortedDurations.length === 0) {
return 0;
}
const rank = (percentile / 100) * (sortedDurations.length - 1);
const lower = Math.floor(rank);
const upper = Math.ceil(rank);
const weight = rank - lower;
if (upper >= sortedDurations.length) {
return sortedDurations[lower];
}
return sortedDurations[lower] + weight * (sortedDurations[upper] - sortedDurations[lower]);
}
function main() {
const args = parseArgs(process.argv);
const cfg = loadConfig(args.config);
const iterations = args.iterations ?? cfg.iterations ?? 5;
const thresholdMs = args.thresholdMs ?? cfg.thresholdMs ?? 5000;
const results = [];
const failures = [];
for (const scenario of cfg.scenarios) {
const scenarioRoot = path.resolve(args.repoRoot, scenario.root);
if (!ensureWithinRepo(args.repoRoot, scenarioRoot)) {
throw new Error(`Scenario root ${scenario.root} escapes repo root ${args.repoRoot}`);
}
if (!fs.existsSync(scenarioRoot)) {
throw new Error(`Scenario root ${scenarioRoot} does not exist`);
}
const matcher = globToRegExp(scenario.matcher.replace(/\\/g, '/'));
const durations = [];
let sampleCount = 0;
for (let attempt = 0; attempt < iterations; attempt++) {
const start = performance.now();
const files = walkFiles(scenarioRoot, matcher);
if (files.length === 0) {
throw new Error(`Scenario ${scenario.id} matched no files`);
}
for (const filePath of files) {
const contents = fs.readFileSync(filePath, 'utf8');
if (scenario.parser === 'node') {
parseNodePackage(contents);
} else if (scenario.parser === 'python') {
parsePythonMetadata(contents);
} else {
throw new Error(`Unknown parser ${scenario.parser} for scenario ${scenario.id}`);
}
}
const end = performance.now();
durations.push(end - start);
sampleCount = files.length;
}
durations.sort((a, b) => a - b);
const mean = durations.reduce((acc, value) => acc + value, 0) / durations.length;
const p95 = percentile(durations, 95);
const max = durations[durations.length - 1];
if (max > thresholdMs) {
failures.push(`${scenario.id} exceeded threshold: ${(max).toFixed(2)} ms > ${thresholdMs} ms`);
}
results.push({
id: scenario.id,
label: scenario.label,
sampleCount,
meanMs: mean,
p95Ms: p95,
maxMs: max,
iterations,
});
}
console.log('Scenario | Count | Mean(ms) | P95(ms) | Max(ms)');
console.log('---------------------------- | ----- | --------- | --------- | ----------');
for (const row of results) {
console.log(formatRow(row));
}
if (args.out) {
const header = 'scenario,iterations,sample_count,mean_ms,p95_ms,max_ms\n';
const csvRows = results
.map((row) =>
[
row.id,
row.iterations,
row.sampleCount,
row.meanMs.toFixed(4),
row.p95Ms.toFixed(4),
row.maxMs.toFixed(4),
].join(',')
)
.join('\n');
fs.writeFileSync(args.out, header + csvRows + '\n', 'utf8');
}
if (failures.length > 0) {
console.error('\nPerformance threshold exceeded:');
for (const failure of failures) {
console.error(` - ${failure}`);
}
process.exitCode = 1;
}
}
if (require.main === module) {
try {
main();
} catch (err) {
console.error(err instanceof Error ? err.message : err);
process.exit(1);
}
}

View File

@@ -3,6 +3,6 @@
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|----|--------|----------|------------|-------------|---------------|
| BENCH-SCANNER-10-001 | DONE | Bench Guild, Scanner Team | SCANNER-ANALYZERS-LANG-10-303 | Analyzer microbench harness (node_modules, site-packages) + baseline CSV. | Harness committed under `bench/Scanner.Analyzers`; baseline CSV recorded; CI job publishes results. |
| BENCH-SCANNER-10-002 | TODO | Bench Guild, Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-301..309 | Wire real language analyzers into bench harness & refresh baselines post-implementation. | Harness executes analyzer assemblies end-to-end; updated baseline committed; CI trend doc linked. |
| BENCH-SCANNER-10-002 | DONE (2025-10-21) | Bench Guild, Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-301..309 | Wire real language analyzers into bench harness & refresh baselines post-implementation. | Harness executes analyzer assemblies end-to-end; updated baseline committed; CI trend doc linked. |
| BENCH-IMPACT-16-001 | TODO | Bench Guild, Scheduler Team | SCHED-IMPACT-16-301 | ImpactIndex throughput bench (resolve 10k productKeys) + RAM profile. | Benchmark script ready; baseline metrics recorded; alert thresholds defined. |
| BENCH-NOTIFY-15-001 | TODO | Bench Guild, Notify Team | NOTIFY-ENGINE-15-301 | Notify dispatch throughput bench (vary rule density) with results CSV. | Bench executed; results stored; regression alert configured. |

View File

@@ -17,4 +17,21 @@ This directory contains deterministic deployment bundles for the core Stella Ops
3. Run `deploy/tools/validate-profiles.sh` (requires Docker CLI and Helm) to ensure the bundles lint and template cleanly.
4. Commit the change alongside any documentation updates (e.g. install guide cross-links).
Maintaining the digest linkage keeps offline/air-gapped installs reproducible and avoids tag drift between environments.
Maintaining the digest linkage keeps offline/air-gapped installs reproducible and avoids tag drift between environments.
## CI smoke checks
The `.gitea/workflows/build-test-deploy.yml` pipeline includes a `notify-smoke` stage that validates scanner event propagation after staging deployments. Configure the following repository secrets (or environment-level secrets) so the job can connect to Redis and the Notify API:
- `NOTIFY_SMOKE_REDIS_DSN` Redis connection string (`redis://user:pass@host:port/db`).
- `NOTIFY_SMOKE_NOTIFY_BASEURL` Base URL for the staging Notify WebService (e.g. `https://notify.stage.stella-ops.internal`).
- `NOTIFY_SMOKE_NOTIFY_TOKEN` OAuth bearer token (service account) with permission to read deliveries.
- `NOTIFY_SMOKE_NOTIFY_TENANT` Tenant identifier used for the smoke validation requests.
- *(Optional)* `NOTIFY_SMOKE_NOTIFY_TENANT_HEADER` Override for the tenant header name (defaults to `X-StellaOps-Tenant`).
Define the following repository variables (or secrets) to drive the assertions performed by the smoke check:
- `NOTIFY_SMOKE_EXPECT_KINDS` Comma-separated event kinds the checker must observe (for example `scanner.report.ready,scanner.scan.completed`).
- `NOTIFY_SMOKE_LOOKBACK_MINUTES` Time window (in minutes) used when scanning the Redis stream for recent events (for example `30`).
All of the above values are required—the workflow fails fast with a descriptive error if any are missing or empty. Provide the variables at the organisation or repository scope before enabling the smoke stage.

View File

@@ -20,12 +20,25 @@ docker compose --env-file dev.env -f docker-compose.dev.yaml config
docker compose --env-file dev.env -f docker-compose.dev.yaml up -d
```
The stage and airgap variants behave the same way—swap the file names accordingly. All profiles expose 443/8443 for the UI and REST APIs, and they share a `stellaops` Docker network scoped to the compose project.
### Updating to a new release
1. Import the new manifest into `deploy/releases/` (see `deploy/README.md`).
2. Update image digests in the relevant Compose file(s).
3. Re-run `docker compose config` to confirm the bundle is deterministic.
The stage and airgap variants behave the same way—swap the file names accordingly. All profiles expose 443/8443 for the UI and REST APIs, and they share a `stellaops` Docker network scoped to the compose project.
### Scanner event stream settings
Scanner WebService can emit signed `scanner.report.*` events to Redis Streams when `SCANNER__EVENTS__ENABLED=true`. Each profile ships environment placeholders you can override in the `.env` file:
- `SCANNER_EVENTS_ENABLED` toggle emission on/off (defaults to `false`).
- `SCANNER_EVENTS_DRIVER` currently only `redis` is supported.
- `SCANNER_EVENTS_DSN` Redis endpoint; leave blank to reuse the queue DSN when it uses `redis://`.
- `SCANNER_EVENTS_STREAM` stream name (`stella.events` by default).
- `SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS` per-publish timeout window (defaults to `5`).
- `SCANNER_EVENTS_MAX_STREAM_LENGTH` max stream length before Redis trims entries (defaults to `10000`).
Helm values mirror the same knobs under each services `env` map (see `deploy/helm/stellaops/values-*.yaml`).
### Updating to a new release
1. Import the new manifest into `deploy/releases/` (see `deploy/README.md`).
2. Update image digests in the relevant Compose file(s).
3. Re-run `docker compose config` to confirm the bundle is deterministic.
Keep digests synchronized between Compose, Helm, and the release manifest to preserve reproducibility guarantees. `deploy/tools/validate-profiles.sh` performs a quick audit.

View File

@@ -136,10 +136,16 @@ services:
- nats
environment:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER}"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD}"
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER}"
SCANNER__STORAGE__S3__ENDPOINT: "http://minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER}"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD}"
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER}"
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-redis}"
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
ports:
- "${SCANNER_WEB_PORT:-8444}:8444"
networks:

View File

@@ -134,10 +134,16 @@ services:
- nats
environment:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER}"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD}"
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER}"
SCANNER__STORAGE__S3__ENDPOINT: "http://minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER}"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD}"
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER}"
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-redis}"
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
ports:
- "${SCANNER_WEB_PORT:-8444}:8444"
networks:

View File

@@ -134,10 +134,16 @@ services:
- nats
environment:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER}"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD}"
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER}"
SCANNER__STORAGE__S3__ENDPOINT: "http://minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "${MINIO_ROOT_USER}"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "${MINIO_ROOT_PASSWORD}"
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER}"
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-redis}"
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
ports:
- "${SCANNER_WEB_PORT:-8444}:8444"
networks:

View File

@@ -10,8 +10,15 @@ SIGNER_POE_INTROSPECT_URL=file:///offline/poe/introspect.json
SIGNER_PORT=8441
ATTESTOR_PORT=8442
CONCELIER_PORT=8445
SCANNER_WEB_PORT=8444
UI_PORT=9443
NATS_CLIENT_PORT=24222
SCANNER_QUEUE_BROKER=nats://nats:4222
AUTHORITY_OFFLINE_CACHE_TOLERANCE=00:45:00
SCANNER_WEB_PORT=8444
UI_PORT=9443
NATS_CLIENT_PORT=24222
SCANNER_QUEUE_BROKER=nats://nats:4222
AUTHORITY_OFFLINE_CACHE_TOLERANCE=00:45:00
SCANNER_EVENTS_ENABLED=false
SCANNER_EVENTS_DRIVER=redis
# Leave SCANNER_EVENTS_DSN empty to inherit the Redis queue DSN when SCANNER_QUEUE_BROKER uses redis://.
SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000

View File

@@ -10,7 +10,14 @@ SIGNER_POE_INTROSPECT_URL=https://licensing.svc.local/introspect
SIGNER_PORT=8441
ATTESTOR_PORT=8442
CONCELIER_PORT=8445
SCANNER_WEB_PORT=8444
UI_PORT=8443
NATS_CLIENT_PORT=4222
SCANNER_QUEUE_BROKER=nats://nats:4222
SCANNER_WEB_PORT=8444
UI_PORT=8443
NATS_CLIENT_PORT=4222
SCANNER_QUEUE_BROKER=nats://nats:4222
SCANNER_EVENTS_ENABLED=false
SCANNER_EVENTS_DRIVER=redis
# Leave SCANNER_EVENTS_DSN empty to inherit the Redis queue DSN when SCANNER_QUEUE_BROKER uses redis://.
SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000

View File

@@ -10,7 +10,14 @@ SIGNER_POE_INTROSPECT_URL=https://licensing.stage.stella-ops.internal/introspect
SIGNER_PORT=8441
ATTESTOR_PORT=8442
CONCELIER_PORT=8445
SCANNER_WEB_PORT=8444
UI_PORT=8443
NATS_CLIENT_PORT=4222
SCANNER_QUEUE_BROKER=nats://nats:4222
SCANNER_WEB_PORT=8444
UI_PORT=8443
NATS_CLIENT_PORT=4222
SCANNER_QUEUE_BROKER=nats://nats:4222
SCANNER_EVENTS_ENABLED=false
SCANNER_EVENTS_DRIVER=redis
# Leave SCANNER_EVENTS_DSN empty to inherit the Redis queue DSN when SCANNER_QUEUE_BROKER uses redis://.
SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000

View File

@@ -97,20 +97,32 @@ services:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:3df8ca21878126758203c1a0444e39fd97f77ddacf04a69685cda9f1e5e94718
service:
port: 8444
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "false"
SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "false"
SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
notify-web:
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
service:

View File

@@ -96,20 +96,32 @@ services:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:e0dfdb087e330585a5953029fb4757f5abdf7610820a085bd61b457dbead9a11
service:
port: 8444
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "false"
SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "false"
SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
notify-web:
image: registry.stella-ops.org/stellaops/notify-web:2025.10.0-edge
service:

View File

@@ -96,21 +96,33 @@ services:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
service:
port: 8444
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-stage"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-stage"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "false"
SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
replicas: 2
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-stage"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
SCANNER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
SCANNER__STORAGE__S3__ACCESSKEYID: "stellaops-stage"
SCANNER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "false"
SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
notify-web:
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
service:

View File

@@ -489,6 +489,71 @@ Returns `202 Accepted` and `Location: /attest/{id}` for async verify.
---
### 2.8 Runtime  Ingest Observer Events *(SCANNER-RUNTIME-12-301)*
```
POST /api/v1/runtime/events
Authorization: Bearer <token with scanner.runtime.ingest>
Content-Type: application/json
```
| Requirement | Details |
|-------------|---------|
| Auth scope | `scanner.runtime.ingest` |
| Batch size | **256** envelopes (`scanner.runtime.maxBatchSize`, configurable) |
| Payload cap | **1MiB** serialized JSON (`scanner.runtime.maxPayloadBytes`) |
| Rate limits | Per-tenant and per-node token buckets (default 200events/s tenant, 50events/s node, burst 200) excess returns **429** with `Retry-After`. |
| TTL | Runtime events retained **45days** by default (`scanner.runtime.eventTtlDays`). |
**Request body**
```json
{
"batchId": "node-a-2025-10-20T15:03:12Z",
"events": [
{
"schemaVersion": "zastava.runtime.event@v1",
"event": {
"eventId": "evt-2f9c02b8",
"when": "2025-10-20T15:03:08Z",
"kind": "ContainerStart",
"tenant": "tenant-alpha",
"node": "cluster-a/node-01",
"runtime": { "engine": "containerd", "version": "1.7.19" },
"workload": {
"platform": "kubernetes",
"namespace": "payments",
"pod": "api-7c9fbbd8b7-ktd84",
"container": "api",
"containerId": "containerd://bead5...",
"imageRef": "ghcr.io/acme/api@sha256:deadbeef"
},
"process": { "pid": 12345, "entrypoint": ["/start.sh", "--serve"] },
"loadedLibs": [
{ "path": "/lib/x86_64-linux-gnu/libssl.so.3", "inode": 123456, "sha256": "abc123..." }
],
"posture": { "imageSigned": true, "sbomReferrer": "present" },
"delta": { "baselineImageDigest": "sha256:deadbeef" },
"evidence": [ { "signal": "proc.maps", "value": "libssl.so.3@0x7f..." } ],
"annotations": { "observerVersion": "1.0.0" }
}
}
]
}
```
**Responses**
| Code | Body | Notes |
|------|------|-------|
| `202 Accepted` | `{ "accepted": 128, "duplicates": 2 }` | Batch persisted; duplicates are ignored via unique `eventId`. |
| `400 Bad Request` | Problem+JSON | Validation failures empty batch, duplicate IDs, unsupported schema version, payload too large. |
| `429 Too Many Requests` | Problem+JSON | Per-tenant/node rate limit exceeded; `Retry-After` header emitted in seconds. |
Persisted documents capture the canonical envelope (`payload` field), tenant/node metadata, and set an automatic TTL on `expiresAt`. Observers should retry rejected batches with exponential backoff honouring the provided `Retry-After` hint.
---
## 3 StellaOps CLI (`stellaops-cli`)
The new CLI is built on **System.CommandLine2.0.0beta5** and mirrors the Concelier backend REST API.
@@ -521,6 +586,9 @@ See `docs/dev/32_AUTH_CLIENT_GUIDE.md` for recommended profiles (online vs. air-
| `stellaops-cli auth <login\|logout\|status\|whoami>` | Manage cached tokens for StellaOps Authority | `auth login --force` (ignore cache)<br>`auth status`<br>`auth whoami` | Uses `StellaOps.Auth.Client`; honours `StellaOps:Authority:*` configuration, stores tokens under `~/.stellaops/tokens` by default, and `whoami` prints subject/scope/expiry |
| `stellaops-cli auth revoke export` | Export the Authority revocation bundle | `--output <directory>` (defaults to CWD) | Writes `revocation-bundle.json`, `.json.jws`, and `.json.sha256`; verifies the digest locally and includes key metadata in the log summary. |
| `stellaops-cli auth revoke verify` | Validate a revocation bundle offline | `--bundle <path>` `--signature <path>` `--key <path>`<br>`--verbose` | Verifies detached JWS signatures, reports the computed SHA-256, and can fall back to cached JWKS when `--key` is omitted. |
| `stellaops-cli offline kit pull` | Download the latest offline kit bundle and manifest | `--bundle-id <id>` (optional)<br>`--destination <dir>`<br>`--overwrite`<br>`--no-resume` | Streams the bundle + manifest from the configured mirror/backend, resumes interrupted downloads, verifies SHA-256, and writes signatures plus a `.metadata.json` manifest alongside the artefacts. |
| `stellaops-cli offline kit import` | Upload an offline kit bundle to the backend | `<bundle.tgz>` (argument)<br>`--manifest <path>`<br>`--bundle-signature <path>`<br>`--manifest-signature <path>` | Validates digests when metadata is present, then posts multipart payloads to `POST /api/offline-kit/import`; logs the submitted import ID/status for air-gapped rollout tracking. |
| `stellaops-cli offline kit status` | Display imported offline kit details | `--json` | Shows bundle id/kind, captured/imported timestamps, digests, and component versions; `--json` emits machine-readable output for scripting. |
| `stellaops-cli config show` | Display resolved configuration | — | Masks secret values; helpful for airgapped installs |
| `stellaops-cli runtime policy test` | Ask Scanner.WebService for runtime verdicts (Webhook parity) | `--image/-i <digest>` (repeatable, comma/space lists supported)<br>`--file/-f <path>`<br>`--namespace/--ns <name>`<br>`--label/-l key=value` (repeatable)<br>`--json` | Posts to `POST /api/v1/scanner/policy/runtime`, deduplicates image digests, and prints TTL/policy revision plus per-image columns for signed state, SBOM referrers, quieted-by metadata, confidence, and Rekor attestation (uuid + verified flag). Accepts newline/whitespace-delimited stdin when piped; `--json` emits the raw response without additional logging. |
@@ -602,6 +670,8 @@ Authority-backed auth workflow:
Tokens live in `~/.stellaops/tokens` unless `StellaOps:Authority:TokenCacheDirectory` overrides it. Cached tokens are reused offline until they expire; the CLI surfaces clear errors if refresh fails.
For offline workflows, configure `StellaOps:Offline:KitsDirectory` (or `STELLAOPS_OFFLINE_KITS_DIR`) to control where bundles, manifests, and metadata are stored, and `StellaOps:Offline:KitMirror` (or `STELLAOPS_OFFLINE_MIRROR_URL`) to override the download base URL when pulling from a mirror.
**Configuration file template**
```jsonc
@@ -614,6 +684,10 @@ Tokens live in `~/.stellaops/tokens` unless `StellaOps:Authority:TokenCacheDirec
"DefaultRunner": "docker",
"ScannerSignaturePublicKeyPath": "",
"ScannerDownloadAttempts": 3,
"Offline": {
"KitsDirectory": "offline-kits",
"KitMirror": "https://get.stella-ops.org/ouk/"
},
"Authority": {
"Url": "https://authority.example.org",
"ClientId": "concelier-cli",

View File

@@ -151,15 +151,35 @@ All administrative calls emit `AuthEventRecord` entries enriched with correlatio
Authority now understands two flavours of sender-constrained OAuth clients:
- **DPoP proof-of-possession** clients sign a `DPoP` header for `/token` requests. Authority validates the JWK thumbprint, HTTP method/URI, and replay window, then stamps the resulting access token with `cnf.jkt` so downstream services can verify the same key is reused.
- Configure under `security.senderConstraints.dpop`. `allowedAlgorithms`, `proofLifetime`, and `replayWindow` are enforced at validation time.
- `security.senderConstraints.dpop.nonce.enabled` enables nonce challenges for high-value audiences (`requiredAudiences`, normalised to case-insensitive strings). When a nonce is required but missing or expired, `/token` replies with `WWW-Authenticate: DPoP error="use_dpop_nonce"` (and, when available, a fresh `DPoP-Nonce` header). Clients must retry with the issued nonce embedded in the proof.
- `security.senderConstraints.dpop.nonce.store` selects `memory` (default) or `redis`. When `redis` is configured, set `security.senderConstraints.dpop.nonce.redisConnectionString` so replicas share nonce issuance and high-value clients avoid replay gaps during failover.
- Declare client `audiences` in bootstrap manifests or plug-in provisioning metadata; Authority now defaults the token `aud` claim and `resource` indicator from this list, which is also used to trigger nonce enforcement for audiences such as `signer` and `attestor`.
- **Mutual TLS clients** client registrations may declare an mTLS binding (`senderConstraint: mtls`). When enabled via `security.senderConstraints.mtls`, Authority validates the presented client certificate against stored bindings (`certificateBindings[]`), optional chain verification, and timing windows. Successful requests embed `cnf.x5t#S256` into the access token so resource servers can enforce the certificate thumbprint.
- Certificate bindings record the certificate thumbprint, optional SANs, subject/issuer metadata, and activation windows. Operators can enforce subject regexes, SAN type allow-lists (`dns`, `uri`, `ip`), trusted certificate authorities, and rotation grace via `security.senderConstraints.mtls.*`.
Both modes persist additional metadata in `authority_tokens`: `senderConstraint` records the enforced policy, while `senderKeyThumbprint` stores the DPoP JWK thumbprint or mTLS certificate hash captured at issuance. Downstream services can rely on these fields (and the corresponding `cnf` claim) when auditing offline copies of the token store.
- **DPoP proof-of-possession** clients sign a `DPoP` header for `/token` requests. Authority validates the JWK thumbprint, HTTP method/URI, and replay window, then stamps the resulting access token with `cnf.jkt` so downstream services can verify the same key is reused.
- Configure under `security.senderConstraints.dpop`. `allowedAlgorithms`, `proofLifetime`, and `replayWindow` are enforced at validation time.
- `security.senderConstraints.dpop.nonce.enabled` enables nonce challenges for high-value audiences (`requiredAudiences`, normalised to case-insensitive strings). When a nonce is required but missing or expired, `/token` replies with `WWW-Authenticate: DPoP error="use_dpop_nonce"` (and, when available, a fresh `DPoP-Nonce` header). Clients must retry with the issued nonce embedded in the proof.
- `security.senderConstraints.dpop.nonce.store` selects `memory` (default) or `redis`. When `redis` is configured, set `security.senderConstraints.dpop.nonce.redisConnectionString` so replicas share nonce issuance and high-value clients avoid replay gaps during failover.
- Example (enabling Redis-backed nonces; adjust audiences per deployment):
```yaml
security:
senderConstraints:
dpop:
enabled: true
proofLifetime: "00:02:00"
replayWindow: "00:05:00"
allowedAlgorithms: [ "ES256", "ES384" ]
nonce:
enabled: true
ttl: "00:10:00"
maxIssuancePerMinute: 120
store: "redis"
redisConnectionString: "redis://authority-redis:6379?ssl=false"
requiredAudiences:
- "signer"
- "attestor"
```
Operators can override any field via environment variables (e.g. `STELLAOPS_AUTHORITY__SECURITY__SENDERCONSTRAINTS__DPOP__NONCE__STORE=redis`).
- Declare client `audiences` in bootstrap manifests or plug-in provisioning metadata; Authority now defaults the token `aud` claim and `resource` indicator from this list, which is also used to trigger nonce enforcement for audiences such as `signer` and `attestor`.
- **Mutual TLS clients** client registrations may declare an mTLS binding (`senderConstraint: mtls`). When enabled via `security.senderConstraints.mtls`, Authority validates the presented client certificate against stored bindings (`certificateBindings[]`), optional chain verification, and timing windows. Successful requests embed `cnf.x5t#S256` into the access token so resource servers can enforce the certificate thumbprint.
- Certificate bindings record the certificate thumbprint, optional SANs, subject/issuer metadata, and activation windows. Operators can enforce subject regexes, SAN type allow-lists (`dns`, `uri`, `ip`), trusted certificate authorities, and rotation grace via `security.senderConstraints.mtls.*`.
Both modes persist additional metadata in `authority_tokens`: `senderConstraint` records the enforced policy, while `senderKeyThumbprint` stores the DPoP JWK thumbprint or mTLS certificate hash captured at issuance. Downstream services can rely on these fields (and the corresponding `cnf` claim) when auditing offline copies of the token store.
## 8. Offline & Sovereign Operation
- **No outbound dependencies:** Authority only contacts MongoDB and local plugins. Discovery and JWKS are cached by clients with offline tolerances (`AllowOfflineCacheFallback`, `OfflineCacheTolerance`). Operators should mirror these responses for air-gapped use.

View File

@@ -55,10 +55,11 @@
##3Test Harness
* **Runner** `perf/run.sh`, accepts `--phase` and `--samples`.
* **Metrics** Prometheus + `jq` extracts; aggregated via `scripts/aggregate.ts`.
* **CI** GitLab CI job *benchmark* publishes JSON to `benchartifacts/`.
* **Visualisation** Grafana dashboard *StellaPerf* (provisioned JSON).
* **Runner** `perf/run.sh`, accepts `--phase` and `--samples`.
* **Language analyzers microbench** `dotnet run --project bench/Scanner.Analyzers/StellaOps.Bench.ScannerAnalyzers/StellaOps.Bench.ScannerAnalyzers.csproj -- --repo-root . --out bench/Scanner.Analyzers/baseline.csv` produces deterministic CSVs for analyzer scenarios (Node today, others as they land).
* **Metrics** Prometheus + `jq` extracts; aggregated via `scripts/aggregate.ts`.
* **CI** GitLab CI job *benchmark* publishes JSON to `benchartifacts/`.
* **Visualisation** Grafana dashboard *StellaPerf* (provisioned JSON).
> **Note** harness mounts `/var/cache/trivy` tmpfs to avoid disk noise.

View File

@@ -37,7 +37,9 @@ cosign verify-blob \
--key https://stella-ops.org/keys/cosign.pub \
--signature stella-ops-offline-kit-<DATE>.tgz.sig \
stella-ops-offline-kit-<DATE>.tgz
````
````
**CLI shortcut.** `stellaops-cli offline kit pull --destination ./offline-kit` downloads the bundle, manifest, and detached signatures in one step, resumes partial transfers, and writes a `.metadata.json` summary for later import.
Verification prints **OK** and the SHA256 digest; crosscheck against the
[changelog](https://git.stella-ops.org/stella-ops/offline-kit/-/releases).
@@ -60,11 +62,22 @@ The manifest enumerates every artefact (`name`, `sha256`, `size`, `capturedAt`)
## 2·Import on the airgapped host
```bash
docker compose --env-file .env \
-f docker-compose.stella-ops.yml \
exec stella-ops \
stella admin import-offline-usage-kit stella-ops-offline-kit-<DATE>.tgz
```
docker compose --env-file .env \
-f docker-compose.stella-ops.yml \
exec stella-ops \
stella admin import-offline-usage-kit stella-ops-offline-kit-<DATE>.tgz
```
Alternatively, run
```bash
stellaops-cli offline kit import stella-ops-offline-kit-<DATE>.tgz \
--manifest offline-manifest-<DATE>.json \
--bundle-signature stella-ops-offline-kit-<DATE>.tgz.sig \
--manifest-signature offline-manifest-<DATE>.json.jws
```
The CLI validates recorded digests (when `.metadata.json` is present) before streaming the multipart payload to `/api/offline-kit/import`.
* The CLI validates the Cosign signature **before** activation.
* Old feeds are kept until the new bundle is fully verified.

View File

@@ -275,24 +275,56 @@ Every StellaOps service that consumes Authority tokens **must**:
```yaml
authority:
issuer: "https://authority.internal"
keys:
algs: [ "EdDSA", "ES256" ]
rotationDays: 60
storage: kms://cluster-kms/authority-signing
tokens:
accessTtlSeconds: 180
enableRefreshTokens: false
clockSkewSeconds: 60
dpop:
enable: true
nonce:
enable: true
ttlSeconds: 600
store: redis
redisConnectionString: "redis://authority-redis:6379?ssl=false"
mtls:
enable: true
caBundleFile: /etc/ssl/mtls/clients-ca.pem
signing:
enabled: true
activeKeyId: "authority-signing-2025"
keyPath: "../certificates/authority-signing-2025.pem"
algorithm: "ES256"
keySource: "file"
security:
rateLimiting:
token:
enabled: true
permitLimit: 30
window: "00:01:00"
queueLimit: 0
authorize:
enabled: true
permitLimit: 60
window: "00:01:00"
queueLimit: 10
internal:
enabled: false
permitLimit: 5
window: "00:01:00"
queueLimit: 0
senderConstraints:
dpop:
enabled: true
allowedAlgorithms: [ "ES256", "ES384" ]
proofLifetime: "00:02:00"
allowedClockSkew: "00:00:30"
replayWindow: "00:05:00"
nonce:
enabled: true
ttl: "00:10:00"
maxIssuancePerMinute: 120
store: "redis"
redisConnectionString: "redis://authority-redis:6379?ssl=false"
requiredAudiences:
- "signer"
- "attestor"
mtls:
enabled: true
requireChainValidation: true
rotationGrace: "00:15:00"
enforceForAudiences:
- "signer"
allowedSanTypes:
- "dns"
- "uri"
allowedCertificateAuthorities:
- "/etc/ssl/mtls/clients-ca.pem"
clients:
- clientId: scanner-web
grantTypes: [ "client_credentials" ]
@@ -407,4 +439,3 @@ Signer validates that `hash(JWK)` in the proof matches `cnf.jkt` in the token.
2. **Add**: mTLSbound tokens for Signer/Attestor; device code for CLI; optional introspection.
3. **Hardening**: DPoP nonce support; full audit pipeline; HA tuning.
4. **UX**: Tenant/installation admin UI; role→scope editors; client bootstrap wizards.

View File

@@ -299,9 +299,9 @@ POST /consensus/search
body: { vulnIds?: string[], productKeys?: string[], policyRevisionId?: string, since?: timestamp, limit?: int, pageToken?: string }
→ { entries[], nextPageToken? }
POST /resolve
body: { purls: string[], vulnIds: string[], policyRevisionId?: string }
→ { results: [ { vulnId, productKey, rollupStatus, sources[] } ] }
POST /excititor/resolve (scope: vex.read)
body: { productKeys?: string[], purls?: string[], vulnerabilityIds: string[], policyRevisionId?: string }
→ { policy, resolvedAt, results: [ { vulnerabilityId, productKey, status, sources[], conflicts[], decisions[], signals?, summary?, envelope: { artifact, contentSignature?, attestation?, attestationEnvelope?, attestationSignature? } } ] }
```
### 7.2 Exports (cacheable snapshots)
@@ -388,12 +388,13 @@ excititor:
With storage configured, the WebService exposes the following ingress and diagnostic APIs:
* `GET /excititor/status` returns the active storage configuration and registered artifact stores.
* `GET /excititor/health` simple liveness probe.
* `POST /excititor/statements` accepts normalized VEX statements and persists them via `IVexClaimStore`; use this for migrations/backfills.
* `GET /excititor/statements/{vulnId}/{productKey}?since=` returns the immutable statement log for a vulnerability/product pair.
Run the ingestion endpoint once after applying migration `20251019-consensus-signals-statements` to repopulate historical statements with the new severity/KEV/EPSS signal fields.
* `GET /excititor/status` returns the active storage configuration and registered artifact stores.
* `GET /excititor/health` simple liveness probe.
* `POST /excititor/statements` accepts normalized VEX statements and persists them via `IVexClaimStore`; use this for migrations/backfills.
* `GET /excititor/statements/{vulnId}/{productKey}?since=` returns the immutable statement log for a vulnerability/product pair.
* `POST /excititor/resolve` requires `vex.read` scope; accepts up to 256 `(vulnId, productKey)` pairs via `productKeys` or `purls` and returns deterministic consensus results, decision telemetry, and a signed envelope (`artifact` digest, optional signer signature, optional attestation metadata + DSSE envelope). Returns **409 Conflict** when the requested `policyRevisionId` mismatches the active snapshot.
Run the ingestion endpoint once after applying migration `20251019-consensus-signals-statements` to repopulate historical statements with the new severity/KEV/EPSS signal fields.
* `weights.ceiling` raises the deterministic clamp applied to provider tiers/overrides (range 1.05.0). Values outside the range are clamped with warnings so operators can spot typos.
* `scoring.alpha` / `scoring.beta` configure KEV/EPSS boosts for the Phase1 → Phase2 scoring pipeline. Defaults (0.25, 0.5) preserve prior behaviour; negative or excessively large values fall back with diagnostics.
@@ -410,7 +411,7 @@ Run the ingestion endpoint once after applying migration `20251019-consensus-sig
---
## 11) Performance & scale
## 11) Performance & scale
* **Targets:**
@@ -423,9 +424,42 @@ Run the ingestion endpoint once after applying migration `20251019-consensus-sig
* WebService handles control APIs; **Worker** background services (same image) execute fetch/normalize in parallel with ratelimits; Mongo writes batched; upserts by natural keys.
* Exports stream straight to S3 (MinIO) with rolling buffers.
* **Caching:**
* `vex.cache` maps query signatures → export; TTL to avoid stampedes; optimistic reuse unless `force`.
* **Caching:**
* `vex.cache` maps query signatures → export; TTL to avoid stampedes; optimistic reuse unless `force`.
### 11.1 Worker TTL refresh controls
Excititor.Worker ships with a background refresh service that re-evaluates stale consensus rows and applies stability dampers before publishing status flips. Operators can tune its behaviour through the following configuration (shown in `appsettings.json` syntax):
```jsonc
{
"Excititor": {
"Worker": {
"Refresh": {
"Enabled": true,
"ConsensusTtl": "02:00:00", // refresh consensus older than 2 hours
"ScanInterval": "00:10:00", // sweep cadence
"ScanBatchSize": 250, // max documents examined per sweep
"Damper": {
"Minimum": "1.00:00:00", // lower bound before status flip publishes
"Maximum": "2.00:00:00", // upper bound guardrail
"DefaultDuration": "1.12:00:00",
"Rules": [
{ "MinWeight": 0.90, "Duration": "1.00:00:00" },
{ "MinWeight": 0.75, "Duration": "1.06:00:00" },
{ "MinWeight": 0.50, "Duration": "1.12:00:00" }
]
}
}
}
}
}
```
* `ConsensusTtl` governs when the worker issues a fresh resolve for cached consensus data.
* `Damper` lengths are clamped between `Minimum`/`Maximum`; duration is bypassed when component fingerprints (`VexProduct.ComponentIdentifiers`) change.
* The same keys are available through environment variables (e.g., `Excititor__Worker__Refresh__ConsensusTtl=02:00:00`).
---
@@ -457,7 +491,7 @@ Run the ingestion endpoint once after applying migration `20251019-consensus-sig
## 14) Integration points
* **Backend Policy Engine** (in Scanner.WebService): calls `POST /resolve` with batched `(purl, vulnId)` pairs to fetch `rollupStatus + sources`.
* **Backend Policy Engine** (in Scanner.WebService): calls `POST /excititor/resolve` (scope `vex.read`) with batched `(purl, vulnId)` pairs to fetch `rollupStatus + sources`.
* **Concelier**: provides alias graph (CVE↔vendor IDs) and may supply VEXadjacent metadata (e.g., KEV flag) for policy escalation.
* **UI**: VEX explorer screens use `/claims/search` and `/consensus/search`; show conflicts & provenance.
* **CLI**: `stellaops vex export --consensus --since 7d --out vex.json` for audits.
@@ -474,7 +508,7 @@ Run the ingestion endpoint once after applying migration `20251019-consensus-sig
## 16) Rollout plan (incremental)
1. **MVP**: OpenVEX + CSAF connectors for 3 major providers (e.g., Red Hat/SUSE/Ubuntu), normalization + consensus + `/resolve`.
1. **MVP**: OpenVEX + CSAF connectors for 3 major providers (e.g., Red Hat/SUSE/Ubuntu), normalization + consensus + `/excititor/resolve`.
2. **Signature policies**: PGP for distros; cosign for OCI.
3. **Exports + optional attestation**.
4. **CycloneDX VEX** connectors; platform claim expansion tables; UI explorer.

View File

@@ -230,7 +230,7 @@ public interface INotifyConnector {
**Channel mapping**:
* Slack: title + blocks, limited to 50 blocks/3000 chars per section; long lists → link to UI.
* Teams: Adaptive Card schema 1.5; fallback text for older channels.
* Teams: Adaptive Card schema 1.5; fallback text for older channels (surfaced as `teams.fallbackText` metadata alongside webhook hash).
* Email: HTML + text; inline table of top N findings, rest behind UI link.
* Webhook: JSON with `event`, `ruleId`, `actionId`, `summary`, `links`, and raw `payload` subset.
@@ -299,7 +299,7 @@ Internal tooling can hit `/internal/notify/<entity>/normalize` to upgrade legacy
* `POST /channels` | `GET /channels` | `GET /channels/{id}` | `PATCH /channels/{id}` | `DELETE /channels/{id}`
* `POST /channels/{id}/test` → send sample message (no rule evaluation); returns `202 Accepted` with rendered preview + metadata (base keys: `channelType`, `target`, `previewProvider`, `traceId` + connector-specific entries); governed by `api.rateLimits:testSend`.
* `GET /channels/{id}/health` → connector selfcheck
* `GET /channels/{id}/health` → connector selfcheck (returns redacted metadata: secret refs hashed, sensitive config keys masked, fallbacks noted via `teams.fallbackText`/`teams.validation.*`)
* **Rules**

View File

@@ -164,7 +164,7 @@ GET /healthz | /readyz | /metrics
### Report events
When `scanner.events.enabled = true`, the WebService serialises the signed report (canonical JSON + DSSE envelope) with `NotifyCanonicalJsonSerializer` and publishes two Redis Stream entries (`scanner.report.ready`, `scanner.scan.completed`) to the configured stream (default `stella.events`). The stream fields carry the whole envelope plus lightweight headers (`kind`, `tenant`, `ts`) so Notify and UI timelines can consume the event bus without recomputing signatures. Publish timeouts and bounded stream length are controlled via `scanner:events:publishTimeoutSeconds` and `scanner:events:maxStreamLength`. If the queue driver is already Redis and no explicit events DSN is provided, the host reuses the queue connection and auto-enables event emission so deployments get live envelopes without extra wiring.
When `scanner.events.enabled = true`, the WebService serialises the signed report (canonical JSON + DSSE envelope) with `NotifyCanonicalJsonSerializer` and publishes two Redis Stream entries (`scanner.report.ready`, `scanner.scan.completed`) to the configured stream (default `stella.events`). The stream fields carry the whole envelope plus lightweight headers (`kind`, `tenant`, `ts`) so Notify and UI timelines can consume the event bus without recomputing signatures. Publish timeouts and bounded stream length are controlled via `scanner:events:publishTimeoutSeconds` and `scanner:events:maxStreamLength`. If the queue driver is already Redis and no explicit events DSN is provided, the host reuses the queue connection and auto-enables event emission so deployments get live envelopes without extra wiring. Compose/Helm bundles expose the same knobs via the `SCANNER__EVENTS__*` environment variables for quick tuning.
---

View File

@@ -286,9 +286,9 @@ POST /consensus/search
body: { vulnIds?: string[], productKeys?: string[], policyRevisionId?: string, since?: timestamp, limit?: int, pageToken?: string }
→ { entries[], nextPageToken? }
POST /resolve
body: { purls: string[], vulnIds: string[], policyRevisionId?: string }
→ { results: [ { vulnId, productKey, rollupStatus, sources[] } ] }
POST /excititor/resolve (scope: vex.read)
body: { productKeys?: string[], purls?: string[], vulnerabilityIds: string[], policyRevisionId?: string }
→ { policy, resolvedAt, results: [ { vulnerabilityId, productKey, status, sources[], conflicts[], decisions[], signals?, summary?, envelope: { artifact, contentSignature?, attestation?, attestationEnvelope?, attestationSignature? } } ] }
```
### 7.2 Exports (cacheable snapshots)
@@ -426,7 +426,7 @@ vexer:
## 14) Integration points
* **Backend Policy Engine** (in Scanner.WebService): calls `POST /resolve` with batched `(purl, vulnId)` pairs to fetch `rollupStatus + sources`.
* **Backend Policy Engine** (in Scanner.WebService): calls `POST /excititor/resolve` (scope `vex.read`) with batched `(purl, vulnId)` pairs to fetch `rollupStatus + sources`.
* **Feedser**: provides alias graph (CVE↔vendor IDs) and may supply VEXadjacent metadata (e.g., KEV flag) for policy escalation.
* **UI**: VEX explorer screens use `/claims/search` and `/consensus/search`; show conflicts & provenance.
* **CLI**: `stellaops vex export --consensus --since 7d --out vex.json` for audits.
@@ -443,7 +443,7 @@ vexer:
## 16) Rollout plan (incremental)
1. **MVP**: OpenVEX + CSAF connectors for 3 major providers (e.g., Red Hat/SUSE/Ubuntu), normalization + consensus + `/resolve`.
1. **MVP**: OpenVEX + CSAF connectors for 3 major providers (e.g., Red Hat/SUSE/Ubuntu), normalization + consensus + `/excititor/resolve`.
2. **Signature policies**: PGP for distros; cosign for OCI.
3. **Exports + optional attestation**.
4. **CycloneDX VEX** connectors; platform claim expansion tables; UI explorer.

View File

@@ -15,6 +15,7 @@
| DOCS-EVENTS-09-004 | DONE (2025-10-19) | Docs Guild, Scanner WebService | SCANNER-EVENTS-15-201 | Refresh scanner event docs to mirror DSSE-backed report fields, document `scanner.scan.completed`, and capture canonical sample validation. | Schemas updated for new payload shape; README references DSSE reuse and validation test; samples align with emitted events. |
| PLATFORM-EVENTS-09-401 | DONE (2025-10-19) | Platform Events Guild | DOCS-EVENTS-09-003 | Embed canonical event samples into contract/integration tests and ensure CI validates payloads against published schemas. | Notify/Scheduler contract suites exercise samples; CI job validates samples with `ajv-cli`; Platform Events changelog notes coverage. |
| RUNTIME-GUILD-09-402 | DONE (2025-10-19) | Runtime Guild | SCANNER-POLICY-09-107 | Confirm Scanner WebService surfaces `quietedFindingCount` and progress hints to runtime consumers; document readiness checklist. | Runtime verification run captures enriched payload; checklist/doc updates merged; stakeholders acknowledge availability. |
| DOCS-CONCELIER-07-201 | TODO | Docs Guild, Concelier WebService | FEEDWEB-DOCS-01-001 | Final editorial review and publish pass for Concelier authority toggle documentation (Quickstart + operator guide). | Review feedback resolved, publish PR merged, release notes updated with documentation pointer. |
| DOCS-RUNTIME-17-004 | TODO | Docs Guild, Runtime Guild | SCANNER-EMIT-17-701, ZASTAVA-OBS-17-005, DEVOPS-REL-17-002 | Document build-id workflows: SBOM exposure, runtime event payloads, debug-store layout, and operator guidance for symbol retrieval. | Architecture + operator docs updated with build-id sections, examples show `readelf` output + debuginfod usage, references linked from Offline Kit/Release guides. |
> Update statuses (TODO/DOING/REVIEW/DONE/BLOCKED) as progress changes. Keep guides in sync with configuration samples under `etc/`.

View File

@@ -11,7 +11,9 @@
- Operator-facing configuration, auditing, and observability.
- Out of scope: PoE enforcement (Signer) and CLI/UI client UX; those teams consume the new capabilities.
> **Status update (2025-10-19):** `ValidateDpopProofHandler`, `AuthorityClientCertificateValidator`, and the supporting storage/audit plumbing now live in `src/StellaOps.Authority`. DPoP proofs populate `cnf.jkt`, mTLS bindings enforce certificate thumbprints via `cnf.x5t#S256`, and token documents persist the sender constraint metadata. In-memory nonce issuance is wired (Redis implementation to follow). Documentation and configuration references were updated (`docs/11_AUTHORITY.md`). Targeted unit/integration tests were added; running the broader test suite is currently blocked by pre-existing `StellaOps.Concelier.Storage.Mongo` build errors.
> **Status update (2025-10-19):** `ValidateDpopProofHandler`, `AuthorityClientCertificateValidator`, and the supporting storage/audit plumbing now live in `src/StellaOps.Authority`. DPoP proofs populate `cnf.jkt`, mTLS bindings enforce certificate thumbprints via `cnf.x5t#S256`, and token documents persist the sender constraint metadata. In-memory nonce issuance is wired (Redis implementation to follow). Documentation and configuration references were updated (`docs/11_AUTHORITY.md`). Targeted unit/integration tests were added; running the broader test suite is currently blocked by pre-existing `StellaOps.Concelier.Storage.Mongo` build errors.
>
> **Status update (2025-10-20):** Redis-backed nonce configuration is exposed through `security.senderConstraints.dpop.nonce` with sample YAML (`etc/authority.yaml.sample`) and architecture docs refreshed. Operator guide now includes concrete Redis/required audiences snippet; nonce challenge regression remains covered by `ValidateDpopProof_IssuesNonceChallenge_WhenNonceMissing`.
## Design Summary
- Extract the existing Scanner `DpopProofValidator` stack into a shared `StellaOps.Auth.Security` library used by Authority and resource servers.

View File

@@ -1,7 +1,7 @@
# Authority Plug-in Scoped Service Coordination
> Created: 2025-10-19 — Plugin Platform Guild & Authority Core
> Status: Scheduled (session confirmed for 2025-10-20 15:0016:00UTC)
# Authority Plug-in Scoped Service Coordination
> Created: 2025-10-19 — Plugin Platform Guild & Authority Core
> Status: Completed (workshop held 2025-10-20 15:0016:05UTC)
This document tracks preparation, agenda, and outcomes for the scoped-service workshop required before implementing PLUGIN-DI-08-002.
@@ -27,9 +27,28 @@ This document tracks preparation, agenda, and outcomes for the scoped-service wo
- Audit background jobs that assume singleton lifetimes.
- Identify plug-in health checks/telemetry surfaces impacted by scoped lifetimes.
### Pre-work References
- _Add links, file paths, or notes here prior to the session._
### Pre-work References
| Focus | Path | Notes |
|-------|------|-------|
| Host DI wiring | `src/StellaOps.Authority/StellaOps.Authority/Program.cs:159` | Startup registers `IAuthorityIdentityProviderRegistry` as a singleton and invokes `AuthorityPluginLoader.RegisterPlugins(...)` before the container is built. Any scoped plugin services will currently be captured in the singleton registry context. |
| Registrar discovery | `src/StellaOps.Authority/StellaOps.Authority/Plugins/AuthorityPluginLoader.cs:46` | Loader instantiates `IAuthorityPluginRegistrar` implementations via `Activator.CreateInstance`, so registrars cannot depend on host services yet. Need agreement on whether to move discovery post-build or introduce `ActivatorUtilities`. |
| Registry aggregation | `src/StellaOps.Authority/StellaOps.Authority/AuthorityIdentityProviderRegistry.cs:16` | Registry caches `IIdentityProviderPlugin` instances at construction time. With scoped lifetimes we must revisit how providers are resolved (factory vs accessor). |
| Standard registrar services | `src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/StandardPluginRegistrar.cs:21` | All plugin services are registered as singletons today (`StandardUserCredentialStore`, `StandardClientProvisioningStore`, hosted bootstrapper). This registrar is our baseline for migrating to scoped bindings. |
| Hosted bootstrapper | `src/StellaOps.Authority/StellaOps.Authority.Plugin.Standard/Bootstrap/StandardPluginBootstrapper.cs:17` | Background job directly consumes `StandardUserCredentialStore`. If the store becomes scoped we will need an `IServiceScopeFactory` bridge. |
| Password grant handler | `src/StellaOps.Authority/StellaOps.Authority/OpenIddict/Handlers/PasswordGrantHandlers.cs:26` | Password flow resolves `IIdentityProviderPlugin` during scoped requests. Scope semantics must ensure credential stores stay cancellation-aware. |
| Client credential handler | `src/StellaOps.Authority/StellaOps.Authority/OpenIddict/Handlers/ClientCredentialsHandlers.cs:21` | Handler fetches provider + `ClientProvisioning` store; confirms need for consistent scoping in both user and client flows. |
## Preliminary Findings — 2025-10-20
- `IAuthorityIdentityProviderRegistry` must stop materialising provider singletons when scoped lifetimes land. Options to evaluate: make the registry itself scoped, convert it to a factory over `IServiceProvider`, or cache lightweight descriptors and resolve implementations on-demand.
- `AuthorityPluginLoader` instantiates registrars without DI support. To let registrars request scoped helpers (e.g. `IServiceScopeFactory`) we may need a two-phase registration: discover types at build time, defer execution until the container is available.
- Hosted bootstrap tasks (e.g. `StandardPluginBootstrapper`) will break if their dependencies become scoped. Workshop should align on using scoped pipelines inside `StartAsync` or shifting bootstrap work to queued jobs.
- Standard plugin stores assume singleton access to Mongo collections and password hashing utilities. If we embrace scoped stores, document thread-safety expectations and reuse of Mongo clients across scopes.
- OpenIddict handlers already run as scoped services; once providers move to scoped lifetimes we must ensure the new resolution path stays cancellation-aware and avoids redundant service resolution per request.
- 2025-10-20 (PLUGIN-DI-08-003): Registry implementation updated to expose metadata + scoped handles; OpenIddict flows, bootstrap endpoints, and `/health` now resolve providers via scoped leases with accompanying test coverage.
- 2025-10-20 (PLUGIN-DI-08-004): Authority plugin loader now instantiates registrars via scoped DI activations and honours `[ServiceBinding]` metadata in plugin assemblies.
- 2025-10-20 (PLUGIN-DI-08-005): `StandardPluginBootstrapper` shifted to scope-per-run execution using `IServiceScopeFactory`, enabling future scoped stores without singleton leaks.
## Draft Agenda
@@ -39,14 +58,20 @@ This document tracks preparation, agenda, and outcomes for the scoped-service wo
4. Action items & owners (10 min) — capture code/docs/test tasks with due dates.
5. Risks & follow-ups (5 min) — dependencies, rollout sequencing.
## Notes
- _Pending coordination session; populate afterwards._
## Notes
- Session opened with recap of scoped-service goals and PLUGIN-DI-08-001 changes, confirming Authority readiness to adopt `[ServiceBinding]` metadata.
- Agreed to treat `IAuthorityIdentityProviderRegistry` as a scoped-factory facade rather than a singleton cache; registry will track descriptors and resolve implementations on-demand per request/worker scope.
- Standard plug-in bootstrap will create scopes via `IServiceScopeFactory` and pass cancellation tokens through to avoid lingering singleton references.
- Authority Plugin Loader will enumerate plug-in assemblies at startup but defer registrar activation until a scoped service provider is available, aligning with PLUGIN-DI-08-004 implementation.
- Follow-up engineering tasks assigned to land PLUGIN-DI-08-002 code path adjustments and Authority host updates before 2025-10-24.
## Action Item Log
| Item | Owner | Due | Status | Notes |
|------|-------|-----|--------|-------|
| Confirm meeting time | Alicia Rivera | 2025-10-19 15:30UTC | DONE | Calendar invite sent; all required attendees accepted |
| Compile Authority plug-in DI entry points | Jasmin Patel | 2025-10-20 | IN PROGRESS | Gather current Authority plug-in registrars, background jobs, and helper factories that assume singleton lifetimes; add the list with file paths to **Pre-work References** in this document before 2025-10-20 12:00UTC. |
| Outline scoped-session pattern for background jobs | Leah Chen | Post-session | BLOCKED | Requires meeting outcomes |
| Confirm meeting time | Alicia Rivera | 2025-10-19 15:30UTC | DONE | Calendar invite sent; all required attendees accepted |
| Compile Authority plug-in DI entry points | Jasmin Patel | 2025-10-20 | DONE (2025-10-20) | Scoped-service touchpoints summarised in **Pre-work References** and **Preliminary Findings** ahead of the workshop. |
| Outline scoped-session pattern for background jobs | Leah Chen | 2025-10-21 | DONE (2025-10-20) | Pattern agreed: bootstrap services must open transient scopes per execution via `IServiceScopeFactory`; document update to follow in PLUGIN-DI-08-002 patch. |
| Update PLUGIN-DI-08-002 implementation plan | Alicia Rivera | 2025-10-21 | DONE (2025-10-20) | Task board + SPRINTS updated with scoped-integration delivery notes and test references. |
| Sync Authority host backlog | Mohan Singh | 2025-10-21 | DONE (2025-10-20) | Authority/Plugin TASKS.md and SPRINTS entries reflect scoped-service completion. |

View File

@@ -1,6 +1,6 @@
# Normalized Versions Rollout Dashboard (Sprint 2 Concelier)
_Status date: 2025-10-12 17:05 UTC_
_Status date: 2025-10-20 19:10 UTC_
This dashboard tracks connector readiness for emitting `AffectedPackage.NormalizedVersions` arrays and highlights upcoming coordination checkpoints. Use it alongside:
@@ -10,30 +10,32 @@ This dashboard tracks connector readiness for emitting `AffectedPackage.Normaliz
## Key milestones
- **2025-10-12** Normalization finalized `SemVerRangeRuleBuilder` API contract (multi-segment comparators + notes), connector review opens.
- **2025-10-17** Connector owners to post fixture PRs showing `NormalizedVersions` arrays (even if feature-flagged).
- **2025-10-18** Merge cross-connector review to validate consistent field usage before enabling union logic.
- **2025-10-21** Cccs and Cisco connectors finalize normalized rule emission and share merge-counter screenshots.
- **2025-10-22** CertBund localisation translator reviewed; blockers escalated if localisation guidance slips.
- **2025-10-23** ICS-CISA confirms SemVer reuse vs new firmware scheme and files Models ticket if needed.
- **2025-10-24** KISA firmware scheme proposal due; Merge provides same-day review.
- **2025-10-25** Merge runs cross-connector validation before enabling normalized-rule union logic by default.
## Connector readiness matrix
| Connector | Owner team | Normalized versions status | Last update | Next action / link |
|-----------|------------|---------------------------|-------------|--------------------|
| Acsc | BE-Conn-ACSC | ❌ Not started mapper pending | 2025-10-11 | Design DTOs + mapper with normalized rule array; see `src/StellaOps.Concelier.Connector.Acsc/TASKS.md`. |
| Cccs | BE-Conn-CCCS | ⚠️ Scheduled helper ready, implementation due 2025-10-21 | 2025-10-19 | Apply Merge-provided trailing-version helper to emit `NormalizedVersions`; update mapper/tests per `src/StellaOps.Concelier.Connector.Cccs/TASKS.md`. |
| CertBund | BE-Conn-CERTBUND | ⚠️ Follow-up translate `versions` strings to normalized rules | 2025-10-19 | Build `bis`/`alle` translator + fixtures before 2025-10-22 per `src/StellaOps.Concelier.Connector.CertBund/TASKS.md`. |
| CertCc | BE-Conn-CERTCC | ⚠️ In progress fetch pipeline DOING | 2025-10-11 | Implement VINCE mapper with SemVer/NEVRA rules; unblock snapshot regeneration; `src/StellaOps.Concelier.Connector.CertCc/TASKS.md`. |
| Kev | BE-Conn-KEV | ✅ Normalized catalog/due-date rules verified | 2025-10-12 | Fixtures reconfirmed via `dotnet test src/StellaOps.Concelier.Connector.Kev.Tests`; `src/StellaOps.Concelier.Connector.Kev/TASKS.md`. |
| Cve | BE-Conn-CVE | ✅ Normalized SemVer rules verified | 2025-10-12 | Snapshot parity green (`dotnet test src/StellaOps.Concelier.Connector.Cve.Tests`); `src/StellaOps.Concelier.Connector.Cve/TASKS.md`. |
| Ghsa | BE-Conn-GHSA | ⚠️ DOING normalized rollout task active | 2025-10-11 18:45 UTC | Wire `SemVerRangeRuleBuilder` + refresh fixtures; `src/StellaOps.Concelier.Connector.Ghsa/TASKS.md`. |
| Osv | BE-Conn-OSV | ✅ SemVer mapper & parity fixtures verified | 2025-10-12 | GHSA parity regression passing (`dotnet test src/StellaOps.Concelier.Connector.Osv.Tests`); `src/StellaOps.Concelier.Connector.Osv/TASKS.md`. |
| Ics.Cisa | BE-Conn-ICS-CISA | ⚠️ Decision pending normalize SemVer exacts or escalate scheme | 2025-10-19 | Promote `SemVerPrimitive` outputs into `NormalizedVersions` or file Models ticket by 2025-10-23 (`src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md`). |
| Kisa | BE-Conn-KISA | ⚠️ Proposal required firmware scheme due 2025-10-24 | 2025-10-19 | Draft `kisa.build` (or equivalent) scheme with Models, then emit normalized rules; track in `src/StellaOps.Concelier.Connector.Kisa/TASKS.md`. |
| Ru.Bdu | BE-Conn-BDU | ✅ Raw scheme emitted | 2025-10-14 | Mapper now writes `ru-bdu.raw` normalized rules with provenance + telemetry; `src/StellaOps.Concelier.Connector.Ru.Bdu/TASKS.md`. |
| Ru.Nkcki | BE-Conn-Nkcki | ❌ Not started mapper TODO | 2025-10-11 | Similar to BDU; ensure Cyrillic provenance preserved; `src/StellaOps.Concelier.Connector.Ru.Nkcki/TASKS.md`. |
| Vndr.Apple | BE-Conn-Apple | ✅ Shipped emitting normalized arrays | 2025-10-11 | Continue fixture/tooling work; `src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md`. |
| Vndr.Cisco | BE-Conn-Cisco | ⚠️ Scheduled normalized rule emission due 2025-10-21 | 2025-10-19 | Use Merge helper to persist `NormalizedVersions` alongside SemVer primitives; see `src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md`. |
| Vndr.Msrc | BE-Conn-MSRC | ✅ Map + normalized build rules landed | 2025-10-15 | `MsrcMapper` emits `msrc.build` normalized rules with CVRF references; see `src/StellaOps.Concelier.Connector.Vndr.Msrc/TASKS.md`. |
| Nvd | BE-Conn-NVD | ⚠️ Needs follow-up mapper complete but normalized array MR pending | 2025-10-11 | Align CVE notes + normalized payload flag; `src/StellaOps.Concelier.Connector.Nvd/TASKS.md`. |
| Acsc | BE-Conn-ACSC | ❌ Not started normalized helper pending relay stability | 2025-10-20 | Prepare builder integration plan for 2025-10-24 kickoff; update `src/StellaOps.Concelier.Connector.Acsc/TASKS.md` once branch opens. |
| Cccs | BE-Conn-CCCS | ⚠️ DOING trailing-version helper MR reviewing (due 2025-10-21) | 2025-10-20 | Land helper + fixture refresh, post merge-counter screenshot; `src/StellaOps.Concelier.Connector.Cccs/TASKS.md`. |
| CertBund | BE-Conn-CERTBUND | ⚠️ In progress localisation translator WIP (due 2025-10-22) | 2025-10-20 | Finish translator + provenance notes, regenerate fixtures; `src/StellaOps.Concelier.Connector.CertBund/TASKS.md`. |
| CertCc | BE-Conn-CERTCC | ✅ Complete `certcc.vendor` rules emitting | 2025-10-20 | Monitor VINCE payload changes; no action. |
| Kev | BE-Conn-KEV | ✅ Complete catalog/due-date rules verified | 2025-10-20 | Routine monitoring only. |
| Cve | BE-Conn-CVE | ✅ Complete SemVer normalized rules live | 2025-10-20 | Keep fixtures in sync as CVE schema evolves. |
| Ghsa | BE-Conn-GHSA | ✅ Complete rollout merged 2025-10-11 | 2025-10-20 | Maintain parity with OSV ecosystems; no action. |
| Osv | BE-Conn-OSV | ✅ Complete normalized rules shipping | 2025-10-20 | Watch for new ecosystems; refresh fixtures as needed. |
| Ics.Cisa | BE-Conn-ICS-CISA | ⚠️ Decision pending exact SemVer promotion due 2025-10-23 | 2025-10-20 | Promote primitives or request new scheme; `src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md`. |
| Kisa | BE-Conn-KISA | ⚠️ Proposal drafting firmware scheme due 2025-10-24 | 2025-10-20 | Finalise `kisa.build` proposal with Models; update mapper/tests; `src/StellaOps.Concelier.Connector.Kisa/TASKS.md`. |
| Ru.Bdu | BE-Conn-BDU | ✅ Complete `ru-bdu.raw` rules live | 2025-10-20 | Continue monitoring UTF-8 handling; no action. |
| Ru.Nkcki | BE-Conn-Nkcki | ✅ Complete normalized rules emitted | 2025-10-20 | Maintain transliteration guidance; no action. |
| Vndr.Apple | BE-Conn-Apple | ✅ Complete normalized arrays emitting | 2025-10-20 | Add beta-channel coverage follow-up; see module README. |
| Vndr.Cisco | BE-Conn-Cisco | ⚠️ DOING normalized promotion branch open (due 2025-10-21) | 2025-10-20 | Merge helper branch, refresh fixtures, post counters; `src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md`. |
| Vndr.Msrc | BE-Conn-MSRC | ✅ Complete `msrc.build` rules emitting | 2025-10-20 | Monitor monthly rollups; no action. |
| Nvd | BE-Conn-NVD | ✅ Complete normalized SemVer output live | 2025-10-20 | Keep provenance aligned with CVE IDs; monitor export parity toggle. |
Legend: ✅ complete, ⚠️ in progress/partial, ❌ not started.

View File

@@ -33,6 +33,20 @@ Key knobs:
Mirror responses carry deterministic cache headers: `/index.json` returns `Cache-Control: public, max-age=60`, while per-domain manifests/bundles include `Cache-Control: public, max-age=300, immutable`. Rate limiting surfaces `Retry-After` when quotas are exceeded.
### 1.2 Mirror connector configuration
Downstream Concelier instances ingest published bundles using the `StellaOpsMirrorConnector`. Operators running the connector in airgapped or limited connectivity environments can tune the following options (environment prefix `CONCELIER__SOURCES__STELLAOPSMIRROR__`):
- `BASEADDRESS` absolute mirror root (e.g., `https://mirror-primary.stella-ops.org`).
- `INDEXPATH` relative path to the mirror index (`/concelier/exports/index.json` by default).
- `DOMAINID` mirror domain identifier from the index (`primary`, `community`, etc.).
- `HTTPTIMEOUT` request timeout; raise when mirrors sit behind slow WAN links.
- `SIGNATURE__ENABLED` require detached JWS verification for `bundle.json`.
- `SIGNATURE__KEYID` / `SIGNATURE__PROVIDER` expected signing key metadata.
- `SIGNATURE__PUBLICKEYPATH` PEM fallback used when the mirror key registry is offline.
The connector keeps a per-export fingerprint (bundle digest + generated-at timestamp) and tracks outstanding document IDs. If a scan is interrupted, the next run resumes parse/map work using the stored fingerprint and pending document lists—no network requests are reissued unless the upstream digest changes.
## 2. Secret & certificate layout
### Docker Compose (`deploy/compose/docker-compose.mirror.yaml`)

View File

@@ -0,0 +1,14 @@
# 2025-10-20 — Authority Identity Provider Registry & DPoP nonce updates
## Summary
- Authority host now resolves identity providers through the new metadata/handle pattern introduced in `StellaOps.Authority.Plugins.Abstractions`. Runtime handlers (`ValidateClientCredentialsHandler`, `ValidatePasswordGrantHandler`, `ValidateAccessTokenHandler`, bootstrap endpoints) acquire providers with `IAuthorityIdentityProviderRegistry.AcquireAsync` and rely on metadata (`AuthorityIdentityProviderMetadata`) for capability checks.
- Unit and integration tests build lightweight `ServiceProvider` instances with test plugins, matching production DI behaviour and ensuring the new registry contract is exercised.
- DPoP nonce enforcement now prefers `NormalizedAudiences` when populated and gracefully falls back to the configured `RequiredAudiences`, eliminating the runtime type mismatch that previously surfaced during test runs.
## Operator impact
- No configuration changes are required; existing YAML and environment-based settings continue to function.
- Documentation examples referencing password/mTLS bootstrap flows remain accurate. The new registry logic simply ensures providers advertised in configuration are resolved deterministically and capability-gated before use.
## Developer notes
- When adding new identity providers or tests, register plugins via `ServiceCollection` and call `new AuthorityIdentityProviderRegistry(serviceProvider, logger)`.
- For DPoP-required endpoints, populate `security.senderConstraints.dpop.nonce.requiredAudiences` or rely on defaults; both now funnel through the normalized set.

View File

@@ -0,0 +1,5 @@
# 2025-10-20 Scanner Platform Events Hardening
- Scanner WebService now wires a reusable `IRedisConnectionFactory`, simplifying redis transport testing and reuse for future adapters.
- `/api/v1/reports` integration test (`ReportsEndpointPublishesPlatformEvents`) asserts both report-ready and scan-completed envelopes carry DSSE payloads, scope metadata, and deterministic verdicts.
- Task `SCANNER-EVENTS-15-201` closed after verifying `dotnet test src/StellaOps.Scanner.WebService.Tests/StellaOps.Scanner.WebService.Tests.csproj`.

View File

@@ -108,6 +108,52 @@ clients:
# CIDR ranges that bypass network-sensitive policies (e.g. on-host cron jobs).
# Keep the list tight: localhost is sufficient for most air-gapped installs.
bypassNetworks:
- "127.0.0.1/32"
- "::1/128"
bypassNetworks:
- "127.0.0.1/32"
- "::1/128"
# Security posture (rate limiting + sender constraints).
security:
rateLimiting:
token:
enabled: true
permitLimit: 30
window: "00:01:00"
queueLimit: 0
authorize:
enabled: true
permitLimit: 60
window: "00:01:00"
queueLimit: 10
internal:
enabled: false
permitLimit: 5
window: "00:01:00"
queueLimit: 0
senderConstraints:
dpop:
enabled: true
allowedAlgorithms: [ "ES256", "ES384" ]
proofLifetime: "00:02:00"
allowedClockSkew: "00:00:30"
replayWindow: "00:05:00"
nonce:
enabled: true
ttl: "00:10:00"
maxIssuancePerMinute: 120
store: "memory" # Set to "redis" for multi-node Authority deployments.
requiredAudiences:
- "signer"
- "attestor"
# redisConnectionString: "redis://authority-redis:6379?ssl=false"
mtls:
enabled: false
requireChainValidation: true
rotationGrace: "00:15:00"
enforceForAudiences:
- "signer"
allowedSanTypes:
- "dns"
- "uri"
allowedCertificateAuthorities: [ ]
allowedSubjectPatterns: [ ]

View File

@@ -3,12 +3,16 @@
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|----|--------|----------|------------|-------------|---------------|
| DEVOPS-HELM-09-001 | DONE | DevOps Guild | SCANNER-WEB-09-101 | Create Helm/Compose environment profiles (dev, staging, airgap) with deterministic digests. | Profiles committed under `deploy/`; docs updated; CI smoke deploy passes. |
| DEVOPS-SCANNER-09-204 | TODO | DevOps Guild, Scanner WebService Guild | SCANNER-EVENTS-15-201 | Surface `SCANNER__EVENTS__*` environment variables across docker-compose (dev/stage/airgap) and Helm values, defaulting to share the Redis queue DSN. | Compose/Helm configs ship enabled Redis event publishing with documented overrides; lint jobs updated; docs cross-link to new knobs. |
| DEVOPS-SCANNER-09-205 | TODO | DevOps Guild, Notify Guild | DEVOPS-SCANNER-09-204 | Add Notify smoke stage that tails the Redis stream and asserts `scanner.report.ready`/`scanner.scan.completed` reach Notify WebService in staging. | CI job reads Redis stream during scanner smoke deploy, confirms Notify ingestion via API, alerts on failure. |
| DEVOPS-SCANNER-09-204 | DONE (2025-10-21) | DevOps Guild, Scanner WebService Guild | SCANNER-EVENTS-15-201 | Surface `SCANNER__EVENTS__*` environment variables across docker-compose (dev/stage/airgap) and Helm values, defaulting to share the Redis queue DSN. | Compose/Helm configs ship enabled Redis event publishing with documented overrides; lint jobs updated; docs cross-link to new knobs. |
| DEVOPS-SCANNER-09-205 | DONE (2025-10-21) | DevOps Guild, Notify Guild | DEVOPS-SCANNER-09-204 | Add Notify smoke stage that tails the Redis stream and asserts `scanner.report.ready`/`scanner.scan.completed` reach Notify WebService in staging. | CI job reads Redis stream during scanner smoke deploy, confirms Notify ingestion via API, alerts on failure. |
| DEVOPS-PERF-10-001 | DONE | DevOps Guild | BENCH-SCANNER-10-001 | Add perf smoke job (SBOM compose <5s target) to CI. | CI job runs sample build verifying <5s; alerts configured. |
| DEVOPS-PERF-10-002 | TODO | DevOps Guild | BENCH-SCANNER-10-002 | Publish analyzer bench metrics to Grafana/perf workbook and alarm on 20% regressions. | CI exports JSON for dashboards; Grafana panel wired; Ops on-call doc updated with alert hook. |
| DEVOPS-REL-14-001 | TODO | DevOps Guild | SIGNER-API-11-101, ATTESTOR-API-11-201 | Deterministic build/release pipeline with SBOM/provenance, signing, manifest generation. | CI pipeline produces signed images + SBOM/attestations, manifests published with verified hashes, docs updated. |
| DEVOPS-REL-17-002 | TODO | DevOps Guild | DEVOPS-REL-14-001, SCANNER-EMIT-17-701 | Persist stripped-debug artifacts organised by GNU build-id and bundle them into release/offline kits with checksum manifests. | CI job writes `.debug` files under `artifacts/debug/.build-id/`, manifest + checksums published, offline kit includes cache, smoke job proves symbol lookup via build-id. |
| DEVOPS-MIRROR-08-001 | DONE (2025-10-19) | DevOps Guild | DEVOPS-REL-14-001 | Stand up managed mirror profiles for `*.stella-ops.org` (Concelier/Excititor), including Helm/Compose overlays, multi-tenant secrets, CDN caching, and sync documentation. | Infra overlays committed, CI smoke deploy hits mirror endpoints, runbooks published for downstream sync and quota management. |
| DEVOPS-REL-17-002 | TODO | DevOps Guild | DEVOPS-REL-14-001, SCANNER-EMIT-17-701 | Persist stripped-debug artifacts organised by GNU build-id and bundle them into release/offline kits with checksum manifests. | CI job writes `.debug` files under `artifacts/debug/.build-id/`, manifest + checksums published, offline kit includes cache, smoke job proves symbol lookup via build-id. |
| DEVOPS-MIRROR-08-001 | DONE (2025-10-19) | DevOps Guild | DEVOPS-REL-14-001 | Stand up managed mirror profiles for `*.stella-ops.org` (Concelier/Excititor), including Helm/Compose overlays, multi-tenant secrets, CDN caching, and sync documentation. | Infra overlays committed, CI smoke deploy hits mirror endpoints, runbooks published for downstream sync and quota management. |
| DEVOPS-SEC-10-301 | DONE (2025-10-20) | DevOps Guild | Wave 0A complete | Address NU1902/NU1903 advisories for `MongoDB.Driver` 2.12.0 and `SharpCompress` 0.23.0 surfaced during scanner cache and worker test runs. | Dependencies bumped to patched releases, audit logs free of NU1902/NU1903 warnings, regression tests green, change log documents upgrade guidance. |
| DEVOPS-LAUNCH-18-100 | TODO | DevOps Guild | - | Finalise production environment footprint (clusters, secrets, network overlays) for full-platform go-live. | IaC/compose overlays committed, secrets placeholders documented, dry-run deploy succeeds in staging. |
| DEVOPS-LAUNCH-18-900 | TODO | DevOps Guild, Module Leads | Wave 0 completion | Collect full implementation sign-off from module owners and consolidate launch readiness checklist. | Sign-off record stored under `docs/ops/launch-readiness.md`; outstanding gaps triaged; checklist approved. |
| DEVOPS-LAUNCH-18-001 | TODO | DevOps Guild | DEVOPS-LAUNCH-18-100, DEVOPS-LAUNCH-18-900 | Production launch cutover rehearsal and runbook publication. | `docs/ops/launch-cutover.md` drafted, rehearsal executed with rollback drill, approvals captured. |
> Remark (2025-10-20): Repacked `Mongo2Go` local feed to require MongoDB.Driver 3.5.0 + SharpCompress 0.41.0; cache regression tests green and NU1902/NU1903 suppressed.
> Remark (2025-10-21): Compose/Helm profiles now surface `SCANNER__EVENTS__*` toggles with docs pointing at new `.env` placeholders.

View File

@@ -0,0 +1,18 @@
{
"schemaVersion": "1.0",
"id": "stellaops.notify.connector.email",
"displayName": "StellaOps Email Notify Connector",
"version": "0.1.0-alpha",
"requiresRestart": true,
"entryPoint": {
"type": "dotnet",
"assembly": "StellaOps.Notify.Connectors.Email.dll"
},
"capabilities": [
"notify-connector",
"email"
],
"metadata": {
"org.stellaops.notify.channel.type": "email"
}
}

View File

@@ -0,0 +1,19 @@
{
"schemaVersion": "1.0",
"id": "stellaops.notify.connector.slack",
"displayName": "StellaOps Slack Notify Connector",
"version": "0.1.0-alpha",
"requiresRestart": true,
"entryPoint": {
"type": "dotnet",
"assembly": "StellaOps.Notify.Connectors.Slack.dll"
},
"capabilities": [
"notify-connector",
"slack"
],
"metadata": {
"org.stellaops.notify.channel.type": "slack",
"org.stellaops.notify.connector.requiredScopes": "chat:write,chat:write.public"
}
}

View File

@@ -0,0 +1,19 @@
{
"schemaVersion": "1.0",
"id": "stellaops.notify.connector.teams",
"displayName": "StellaOps Teams Notify Connector",
"version": "0.1.0-alpha",
"requiresRestart": true,
"entryPoint": {
"type": "dotnet",
"assembly": "StellaOps.Notify.Connectors.Teams.dll"
},
"capabilities": [
"notify-connector",
"teams"
],
"metadata": {
"org.stellaops.notify.channel.type": "teams",
"org.stellaops.notify.connector.cardVersion": "1.5"
}
}

View File

@@ -0,0 +1,18 @@
{
"schemaVersion": "1.0",
"id": "stellaops.notify.connector.webhook",
"displayName": "StellaOps Webhook Notify Connector",
"version": "0.1.0-alpha",
"requiresRestart": true,
"entryPoint": {
"type": "dotnet",
"assembly": "StellaOps.Notify.Connectors.Webhook.dll"
},
"capabilities": [
"notify-connector",
"webhook"
],
"metadata": {
"org.stellaops.notify.channel.type": "webhook"
}
}

View File

@@ -1,6 +1,7 @@
# Runtime Fixtures
Supporting filesystem snippets consumed by analyzer microbenchmarks and integration tests. They are intentionally lightweight yet deterministic so they can be committed to the repository without bloating history.
- `npm-monorepo/` trimmed `node_modules/` tree for workspace-style Node.js projects.
- `python-venv/` selected `site-packages/` entries highlighting `*.dist-info` metadata.
Supporting filesystem snippets consumed by analyzer microbenchmarks and integration tests. They are intentionally lightweight yet deterministic so they can be committed to the repository without bloating history.
- `npm-monorepo/` trimmed `node_modules/` tree for workspace-style Node.js projects.
- `java-demo/` minimal `libs/` directory with a tiny Maven-style JAR for the Java analyzer.
- `python-venv/` selected `site-packages/` entries highlighting `*.dist-info` metadata.

View File

@@ -0,0 +1,5 @@
# Java Demo Fixture
Minimal archive tree that exercises the Java language analyzer during microbenchmarks. The `libs/demo.jar`
artefact ships `META-INF/MANIFEST.MF` and `META-INF/maven/com.example/demo/pom.properties` entries so the
analyzer can extract Maven coordinates and manifest metadata without pulling in large third-party jars.

Binary file not shown.

View File

@@ -0,0 +1,57 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$repoRoot = Split-Path -Parent $PSScriptRoot
$pluginsDir = Join-Path $repoRoot 'plugins\notify'
$assemblies = @{
slack = 'StellaOps.Notify.Connectors.Slack.dll'
teams = 'StellaOps.Notify.Connectors.Teams.dll'
email = 'StellaOps.Notify.Connectors.Email.dll'
webhook = 'StellaOps.Notify.Connectors.Webhook.dll'
}
$hasFailures = $false
foreach ($channel in $assemblies.Keys) {
$dir = Join-Path $pluginsDir $channel
if (-not (Test-Path -LiteralPath $dir -PathType Container)) {
Write-Host "ERROR: Missing plug-in directory '$dir'."
$hasFailures = $true
continue
}
$manifest = Join-Path $dir 'notify-plugin.json'
$assembly = Join-Path $dir $assemblies[$channel]
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($assemblies[$channel])
$pdb = Join-Path $dir "$baseName.pdb"
$deps = Join-Path $dir "$baseName.deps.json"
if (-not (Test-Path -LiteralPath $manifest -PathType Leaf)) {
Write-Host "ERROR: Missing manifest for '$channel' connector ($manifest)."
$hasFailures = $true
}
if (-not (Test-Path -LiteralPath $assembly -PathType Leaf)) {
Write-Host "ERROR: Missing assembly for '$channel' connector ($assembly)."
$hasFailures = $true
}
Get-ChildItem -LiteralPath $dir -File | ForEach-Object {
switch ($_.Name) {
'notify-plugin.json' { return }
{ $_.Name -eq $assemblies[$channel] } { return }
{ $_.Name -eq "$baseName.pdb" } { return }
{ $_.Name -eq "$baseName.deps.json" } { return }
default {
Write-Host "ERROR: Unexpected file '$($_.Name)' in '$dir'."
$hasFailures = $true
}
}
}
}
if ($hasFailures) {
exit 1
}
exit 0

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
plugins_dir="${repo_root}/plugins/notify"
declare -A assemblies=(
[slack]="StellaOps.Notify.Connectors.Slack.dll"
[teams]="StellaOps.Notify.Connectors.Teams.dll"
[email]="StellaOps.Notify.Connectors.Email.dll"
[webhook]="StellaOps.Notify.Connectors.Webhook.dll"
)
status=0
for channel in "${!assemblies[@]}"; do
dir="${plugins_dir}/${channel}"
if [[ ! -d "${dir}" ]]; then
echo "ERROR: Missing plug-in directory '${dir}'."
status=1
continue
fi
manifest="${dir}/notify-plugin.json"
assembly="${dir}/${assemblies[$channel]}"
base="${assemblies[$channel]%.dll}"
pdb="${dir}/${base}.pdb"
deps="${dir}/${base}.deps.json"
if [[ ! -f "${manifest}" ]]; then
echo "ERROR: Missing manifest for '${channel}' connector (${manifest})."
status=1
fi
if [[ ! -f "${assembly}" ]]; then
echo "ERROR: Missing assembly for '${channel}' connector (${assembly})."
status=1
fi
while IFS= read -r -d '' file; do
name="$(basename "${file}")"
case "${name}" in
"notify-plugin.json" \
| "${assemblies[$channel]}" \
| "${base}.pdb" \
| "${base}.deps.json")
;;
*)
echo "ERROR: Unexpected file '${name}' in '${dir}'."
status=1
;;
esac
done < <(find "${dir}" -maxdepth 1 -type f -print0)
done
exit "${status}"

View File

@@ -7,6 +7,10 @@
<IsConcelierPlugin Condition="'$(IsConcelierPlugin)' == '' and $([System.String]::Copy('$(MSBuildProjectName)').StartsWith('StellaOps.Concelier.Connector.'))">true</IsConcelierPlugin>
<IsConcelierPlugin Condition="'$(IsConcelierPlugin)' == '' and $([System.String]::Copy('$(MSBuildProjectName)').StartsWith('StellaOps.Concelier.Exporter.'))">true</IsConcelierPlugin>
<IsAuthorityPlugin Condition="'$(IsAuthorityPlugin)' == '' and $([System.String]::Copy('$(MSBuildProjectName)').StartsWith('StellaOps.Authority.Plugin.'))">true</IsAuthorityPlugin>
<NotifyPluginOutputRoot Condition="'$(NotifyPluginOutputRoot)' == '' and '$(SolutionDir)' != ''">$(SolutionDir)plugins\notify</NotifyPluginOutputRoot>
<NotifyPluginOutputRoot Condition="'$(NotifyPluginOutputRoot)' == '' and '$(SolutionDir)' == ''">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\plugins\notify\'))</NotifyPluginOutputRoot>
<IsNotifyPlugin Condition="'$(IsNotifyPlugin)' == '' and $([System.String]::Copy('$(MSBuildProjectName)').StartsWith('StellaOps.Notify.Connectors.')) and !$([System.String]::Copy('$(MSBuildProjectName)').EndsWith('.Tests'))">true</IsNotifyPlugin>
<IsNotifyPlugin Condition="'$(IsNotifyPlugin)' == 'true' and $([System.String]::Copy('$(MSBuildProjectName)')) == 'StellaOps.Notify.Connectors.Shared'">false</IsNotifyPlugin>
<ScannerBuildxPluginOutputRoot Condition="'$(ScannerBuildxPluginOutputRoot)' == ''">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\plugins\scanner\buildx\'))</ScannerBuildxPluginOutputRoot>
<IsScannerBuildxPlugin Condition="'$(IsScannerBuildxPlugin)' == '' and $([System.String]::Copy('$(MSBuildProjectName)')) == 'StellaOps.Scanner.Sbomer.BuildXPlugin'">true</IsScannerBuildxPlugin>
<ScannerOsAnalyzerPluginOutputRoot Condition="'$(ScannerOsAnalyzerPluginOutputRoot)' == ''">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\plugins\scanner\analyzers\os\'))</ScannerOsAnalyzerPluginOutputRoot>

View File

@@ -31,6 +31,24 @@
<Copy SourceFiles="@(AuthorityPluginArtifacts)" DestinationFolder="$(AuthorityPluginOutputDirectory)" SkipUnchangedFiles="true" />
</Target>
<Target Name="NotifyCopyPluginArtifacts" AfterTargets="Build" Condition="'$(IsNotifyPlugin)' == 'true'">
<PropertyGroup>
<NotifyPluginDirectoryName>$([System.String]::Copy('$(MSBuildProjectName)').Replace('StellaOps.Notify.Connectors.', '').ToLowerInvariant())</NotifyPluginDirectoryName>
<NotifyPluginOutputDirectory>$(NotifyPluginOutputRoot)\$(NotifyPluginDirectoryName)</NotifyPluginOutputDirectory>
</PropertyGroup>
<MakeDir Directories="$(NotifyPluginOutputDirectory)" />
<ItemGroup>
<NotifyPluginArtifacts Include="$(TargetPath)" />
<NotifyPluginArtifacts Include="$(TargetPath).deps.json" Condition="Exists('$(TargetPath).deps.json')" />
<NotifyPluginArtifacts Include="$(TargetDir)$(TargetName).pdb" Condition="Exists('$(TargetDir)$(TargetName).pdb')" />
<NotifyPluginArtifacts Include="$(ProjectDir)notify-plugin.json" Condition="Exists('$(ProjectDir)notify-plugin.json')" />
</ItemGroup>
<Copy SourceFiles="@(NotifyPluginArtifacts)" DestinationFolder="$(NotifyPluginOutputDirectory)" SkipUnchangedFiles="true" />
</Target>
<Target Name="ScannerCopyBuildxPluginArtifacts" AfterTargets="Build" Condition="'$(IsScannerBuildxPlugin)' == 'true'">
<PropertyGroup>
<ScannerBuildxPluginOutputDirectory>$(ScannerBuildxPluginOutputRoot)\$(MSBuildProjectName)</ScannerBuildxPluginOutputDirectory>

View File

@@ -78,7 +78,7 @@ public class StandardPluginRegistrarTests
var registrar = new StandardPluginRegistrar();
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
var provider = services.BuildServiceProvider();
using var provider = services.BuildServiceProvider();
var hostedServices = provider.GetServices<IHostedService>();
foreach (var hosted in hostedServices)
{
@@ -88,7 +88,8 @@ public class StandardPluginRegistrarTests
}
}
var plugin = provider.GetRequiredService<IIdentityProviderPlugin>();
using var scope = provider.CreateScope();
var plugin = scope.ServiceProvider.GetRequiredService<IIdentityProviderPlugin>();
Assert.Equal("standard", plugin.Type);
Assert.True(plugin.Capabilities.SupportsPassword);
Assert.False(plugin.Capabilities.SupportsMfa);
@@ -138,7 +139,8 @@ public class StandardPluginRegistrarTests
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
using var provider = services.BuildServiceProvider();
_ = provider.GetRequiredService<StandardUserCredentialStore>();
using var scope = provider.CreateScope();
_ = scope.ServiceProvider.GetRequiredService<StandardUserCredentialStore>();
Assert.Contains(loggerProvider.Entries, entry =>
entry.Level == LogLevel.Warning &&
@@ -176,7 +178,8 @@ public class StandardPluginRegistrarTests
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
using var provider = services.BuildServiceProvider();
var plugin = provider.GetRequiredService<IIdentityProviderPlugin>();
using var scope = provider.CreateScope();
var plugin = scope.ServiceProvider.GetRequiredService<IIdentityProviderPlugin>();
Assert.True(plugin.Capabilities.SupportsPassword);
}
@@ -215,7 +218,8 @@ public class StandardPluginRegistrarTests
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
using var provider = services.BuildServiceProvider();
Assert.Throws<InvalidOperationException>(() => provider.GetRequiredService<IIdentityProviderPlugin>());
using var scope = provider.CreateScope();
Assert.Throws<InvalidOperationException>(() => scope.ServiceProvider.GetRequiredService<IIdentityProviderPlugin>());
}
[Fact]

View File

@@ -1,5 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -10,24 +11,25 @@ namespace StellaOps.Authority.Plugin.Standard.Bootstrap;
internal sealed class StandardPluginBootstrapper : IHostedService
{
private readonly string pluginName;
private readonly IOptionsMonitor<StandardPluginOptions> optionsMonitor;
private readonly StandardUserCredentialStore credentialStore;
private readonly IServiceScopeFactory scopeFactory;
private readonly ILogger<StandardPluginBootstrapper> logger;
public StandardPluginBootstrapper(
string pluginName,
IOptionsMonitor<StandardPluginOptions> optionsMonitor,
StandardUserCredentialStore credentialStore,
IServiceScopeFactory scopeFactory,
ILogger<StandardPluginBootstrapper> logger)
{
this.pluginName = pluginName;
this.optionsMonitor = optionsMonitor;
this.credentialStore = credentialStore;
this.scopeFactory = scopeFactory;
this.logger = logger;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = scopeFactory.CreateScope();
var optionsMonitor = scope.ServiceProvider.GetRequiredService<IOptionsMonitor<StandardPluginOptions>>();
var credentialStore = scope.ServiceProvider.GetRequiredService<StandardUserCredentialStore>();
var options = optionsMonitor.Get(pluginName);
if (options.BootstrapUser is null || !options.BootstrapUser.IsConfigured)
{

View File

@@ -43,7 +43,7 @@ internal sealed class StandardPluginRegistrar : IAuthorityPluginRegistrar
})
.ValidateOnStart();
context.Services.AddSingleton(sp =>
context.Services.AddScoped(sp =>
{
var database = sp.GetRequiredService<IMongoDatabase>();
var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<StandardPluginOptions>>();
@@ -79,7 +79,7 @@ internal sealed class StandardPluginRegistrar : IAuthorityPluginRegistrar
loggerFactory.CreateLogger<StandardUserCredentialStore>());
});
context.Services.AddSingleton(sp =>
context.Services.AddScoped(sp =>
{
var clientStore = sp.GetRequiredService<IAuthorityClientStore>();
var revocationStore = sp.GetRequiredService<IAuthorityRevocationStore>();
@@ -87,7 +87,7 @@ internal sealed class StandardPluginRegistrar : IAuthorityPluginRegistrar
return new StandardClientProvisioningStore(pluginName, clientStore, revocationStore, timeProvider);
});
context.Services.AddSingleton<IIdentityProviderPlugin>(sp =>
context.Services.AddScoped<IIdentityProviderPlugin>(sp =>
{
var store = sp.GetRequiredService<StandardUserCredentialStore>();
var clientProvisioningStore = sp.GetRequiredService<StandardClientProvisioningStore>();
@@ -100,14 +100,13 @@ internal sealed class StandardPluginRegistrar : IAuthorityPluginRegistrar
loggerFactory.CreateLogger<StandardIdentityProviderPlugin>());
});
context.Services.AddSingleton<IClientProvisioningStore>(sp =>
context.Services.AddScoped<IClientProvisioningStore>(sp =>
sp.GetRequiredService<StandardClientProvisioningStore>());
context.Services.AddSingleton<IHostedService>(sp =>
new StandardPluginBootstrapper(
pluginName,
sp.GetRequiredService<IOptionsMonitor<StandardPluginOptions>>(),
sp.GetRequiredService<StandardUserCredentialStore>(),
sp.GetRequiredService<IServiceScopeFactory>(),
sp.GetRequiredService<ILogger<StandardPluginBootstrapper>>()));
}
}

View File

@@ -5,10 +5,10 @@
| PLG6.DOC | DONE (2025-10-11) | BE-Auth Plugin, Docs Guild | PLG1PLG5 | Final polish + diagrams for plugin developer guide (AUTHPLUG-DOCS-01-001). | Docs team delivers copy-edit + exported diagrams; PR merged. |
| SEC1.PLG | DONE (2025-10-11) | Security Guild, BE-Auth Plugin | SEC1.A (StellaOps.Cryptography) | Swap Standard plugin hashing to Argon2id via `StellaOps.Cryptography` abstractions; keep PBKDF2 verification for legacy. | ✅ `StandardUserCredentialStore` uses `ICryptoProvider` to hash/check; ✅ Transparent rehash on success; ✅ Unit tests cover tamper + legacy rehash. |
| SEC1.OPT | DONE (2025-10-11) | Security Guild | SEC1.PLG | Expose password hashing knobs in `StandardPluginOptions` (`memoryKiB`, `iterations`, `parallelism`, `algorithm`) with validation. | ✅ Options bound from YAML; ✅ Invalid configs throw; ✅ Docs include tuning guidance. |
| SEC2.PLG | DOING (2025-10-14) | Security Guild, Storage Guild | SEC2.A (audit contract) | Emit audit events from password verification outcomes and persist via `IAuthorityLoginAttemptStore`. <br>⏳ Awaiting AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 completion to unlock Wave0B verification paths. | ✅ Serilog events enriched with subject/client/IP/outcome; ✅ Mongo records written per attempt; ✅ Tests assert success/lockout/failure cases. |
| SEC3.PLG | DOING (2025-10-14) | Security Guild, BE-Auth Plugin | CORE8, SEC3.A (rate limiter) | Ensure lockout responses and rate-limit metadata flow through plugin logs/events (include retry-after). <br> Pending AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 so limiter telemetry contract matches final authority surface. | ✅ Audit record includes retry-after; ✅ Tests confirm lockout + limiter interplay. |
| SEC2.PLG | BLOCKED (2025-10-21) | Security Guild, Storage Guild | SEC2.A (audit contract) | Emit audit events from password verification outcomes and persist via `IAuthorityLoginAttemptStore`. <br>⛔ Waiting on AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 to stabilise Authority auth surfaces before final verification + publish. | ✅ Serilog events enriched with subject/client/IP/outcome; ✅ Mongo records written per attempt; ✅ Tests assert success/lockout/failure cases. |
| SEC3.PLG | BLOCKED (2025-10-21) | Security Guild, BE-Auth Plugin | CORE8, SEC3.A (rate limiter) | Ensure lockout responses and rate-limit metadata flow through plugin logs/events (include retry-after). <br> Pending AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 so limiter telemetry contract matches final authority surface. | ✅ Audit record includes retry-after; ✅ Tests confirm lockout + limiter interplay. |
| SEC4.PLG | DONE (2025-10-12) | Security Guild | SEC4.A (revocation schema) | Provide plugin hooks so revoked users/clients write reasons for revocation bundle export. | ✅ Revocation exporter consumes plugin data; ✅ Tests cover revoked user/client output. |
| SEC5.PLG | DOING (2025-10-14) | Security Guild | SEC5.A (threat model) | Address plugin-specific mitigations (bootstrap user handling, password policy docs) in threat model backlog. <br> Final documentation depends on AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 outcomes. | ✅ Threat model lists plugin attack surfaces; ✅ Mitigation items filed. |
| SEC5.PLG | BLOCKED (2025-10-21) | Security Guild | SEC5.A (threat model) | Address plugin-specific mitigations (bootstrap user handling, password policy docs) in threat model backlog. <br> Final documentation depends on AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 outcomes. | ✅ Threat model lists plugin attack surfaces; ✅ Mitigation items filed. |
| PLG4-6.CAPABILITIES | BLOCKED (2025-10-12) | BE-Auth Plugin, Docs Guild | PLG1PLG3 | Finalise capability metadata exposure, config validation, and developer guide updates; remaining action is Docs polish/diagram export. | ✅ Capability metadata + validation merged; ✅ Plugin guide updated with final copy & diagrams; ✅ Release notes mention new toggles. <br>⛔ Blocked awaiting Authority rate-limiter stream (CORE8/SEC3) to resume so doc updates reflect final limiter behaviour. |
| PLG7.RFC | REVIEW | BE-Auth Plugin, Security Guild | PLG4 | Socialize LDAP plugin RFC (`docs/rfcs/authority-plugin-ldap.md`) and capture guild feedback. | ✅ Guild review sign-off recorded; ✅ Follow-up issues filed in module boards. |
| PLG6.DIAGRAM | TODO | Docs Guild | PLG6.DOC | Export final sequence/component diagrams for the developer guide and add offline-friendly assets under `docs/assets/authority`. | ✅ Mermaid sources committed; ✅ Rendered SVG/PNG linked from Section 2 + Section 9; ✅ Docs build preview shared with Plugin + Docs guilds. |

View File

@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace StellaOps.Authority.Plugins.Abstractions;
@@ -95,24 +98,24 @@ public interface IAuthorityPluginRegistry
public interface IAuthorityIdentityProviderRegistry
{
/// <summary>
/// Gets all registered identity provider plugins keyed by logical name.
/// Gets metadata for all registered identity provider plugins.
/// </summary>
IReadOnlyCollection<IIdentityProviderPlugin> Providers { get; }
IReadOnlyCollection<AuthorityIdentityProviderMetadata> Providers { get; }
/// <summary>
/// Gets identity providers that advertise password support.
/// Gets metadata for identity providers that advertise password support.
/// </summary>
IReadOnlyCollection<IIdentityProviderPlugin> PasswordProviders { get; }
IReadOnlyCollection<AuthorityIdentityProviderMetadata> PasswordProviders { get; }
/// <summary>
/// Gets identity providers that advertise multi-factor authentication support.
/// Gets metadata for identity providers that advertise multi-factor authentication support.
/// </summary>
IReadOnlyCollection<IIdentityProviderPlugin> MfaProviders { get; }
IReadOnlyCollection<AuthorityIdentityProviderMetadata> MfaProviders { get; }
/// <summary>
/// Gets identity providers that advertise client provisioning support.
/// Gets metadata for identity providers that advertise client provisioning support.
/// </summary>
IReadOnlyCollection<IIdentityProviderPlugin> ClientProvisioningProviders { get; }
IReadOnlyCollection<AuthorityIdentityProviderMetadata> ClientProvisioningProviders { get; }
/// <summary>
/// Aggregate capability flags across all registered providers.
@@ -120,20 +123,89 @@ public interface IAuthorityIdentityProviderRegistry
AuthorityIdentityProviderCapabilities AggregateCapabilities { get; }
/// <summary>
/// Attempts to resolve an identity provider by name.
/// Attempts to resolve identity provider metadata by name.
/// </summary>
bool TryGet(string name, [NotNullWhen(true)] out IIdentityProviderPlugin? provider);
bool TryGet(string name, [NotNullWhen(true)] out AuthorityIdentityProviderMetadata? metadata);
/// <summary>
/// Resolves an identity provider by name or throws when not found.
/// Resolves identity provider metadata by name or throws when not found.
/// </summary>
IIdentityProviderPlugin GetRequired(string name)
AuthorityIdentityProviderMetadata GetRequired(string name)
{
if (TryGet(name, out var provider))
if (TryGet(name, out var metadata))
{
return provider;
return metadata;
}
throw new KeyNotFoundException($"Identity provider plugin '{name}' is not registered.");
}
/// <summary>
/// Acquires a scoped handle to the specified identity provider.
/// </summary>
/// <param name="name">Logical provider name.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Handle managing the provider instance lifetime.</returns>
ValueTask<AuthorityIdentityProviderHandle> AcquireAsync(string name, CancellationToken cancellationToken);
}
/// <summary>
/// Immutable metadata describing a registered identity provider.
/// </summary>
/// <param name="Name">Logical provider name from the manifest.</param>
/// <param name="Type">Provider type identifier.</param>
/// <param name="Capabilities">Capability flags advertised by the provider.</param>
public sealed record AuthorityIdentityProviderMetadata(
string Name,
string Type,
AuthorityIdentityProviderCapabilities Capabilities);
/// <summary>
/// Represents a scoped identity provider instance and manages its disposal.
/// </summary>
public sealed class AuthorityIdentityProviderHandle : IAsyncDisposable, IDisposable
{
private readonly AsyncServiceScope scope;
private bool disposed;
public AuthorityIdentityProviderHandle(AsyncServiceScope scope, AuthorityIdentityProviderMetadata metadata, IIdentityProviderPlugin provider)
{
this.scope = scope;
Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
Provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
/// <summary>
/// Gets the metadata associated with the provider instance.
/// </summary>
public AuthorityIdentityProviderMetadata Metadata { get; }
/// <summary>
/// Gets the active provider instance.
/// </summary>
public IIdentityProviderPlugin Provider { get; }
/// <inheritdoc />
public void Dispose()
{
if (disposed)
{
return;
}
disposed = true;
scope.Dispose();
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
if (disposed)
{
return;
}
disposed = true;
await scope.DisposeAsync().ConfigureAwait(false);
}
}

View File

@@ -1,15 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Authority.Plugins.Abstractions;
using Xunit;
using System.Linq;
namespace StellaOps.Authority.Tests.Identity;
public class AuthorityIdentityProviderRegistryTests
{
[Fact]
public void RegistryIndexesProvidersAndAggregatesCapabilities()
public async Task RegistryIndexesProvidersAndAggregatesCapabilities()
{
var providers = new[]
{
@@ -17,21 +22,25 @@ public class AuthorityIdentityProviderRegistryTests
CreateProvider("sso", type: "saml", supportsPassword: false, supportsMfa: true, supportsClientProvisioning: true)
};
var registry = new AuthorityIdentityProviderRegistry(providers, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
using var serviceProvider = BuildServiceProvider(providers);
var registry = new AuthorityIdentityProviderRegistry(serviceProvider, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
Assert.Equal(2, registry.Providers.Count);
Assert.True(registry.TryGet("standard", out var standard));
Assert.Same(providers[0], standard);
Assert.Equal("standard", standard!.Name);
Assert.Single(registry.PasswordProviders);
Assert.Single(registry.MfaProviders);
Assert.Single(registry.ClientProvisioningProviders);
Assert.True(registry.AggregateCapabilities.SupportsPassword);
Assert.True(registry.AggregateCapabilities.SupportsMfa);
Assert.True(registry.AggregateCapabilities.SupportsClientProvisioning);
await using var handle = await registry.AcquireAsync("standard", default);
Assert.Same(providers[0], handle.Provider);
}
[Fact]
public void RegistryIgnoresDuplicateNames()
public async Task RegistryIgnoresDuplicateNames()
{
var duplicate = CreateProvider("standard", "ldap", supportsPassword: true, supportsMfa: false, supportsClientProvisioning: false);
var providers = new[]
@@ -40,12 +49,56 @@ public class AuthorityIdentityProviderRegistryTests
duplicate
};
var registry = new AuthorityIdentityProviderRegistry(providers, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
using var serviceProvider = BuildServiceProvider(providers);
var registry = new AuthorityIdentityProviderRegistry(serviceProvider, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
Assert.Single(registry.Providers);
Assert.Same(providers[0], registry.Providers.First());
Assert.Equal("standard", registry.Providers.First().Name);
Assert.True(registry.TryGet("standard", out var provider));
Assert.Same(providers[0], provider);
await using var handle = await registry.AcquireAsync("standard", default);
Assert.Same(providers[0], handle.Provider);
Assert.Equal("standard", provider!.Name);
}
[Fact]
public async Task AcquireAsync_ReturnsScopedProviderInstances()
{
var configuration = new ConfigurationBuilder().Build();
var manifest = new AuthorityPluginManifest(
"scoped",
"scoped",
true,
AssemblyName: null,
AssemblyPath: null,
Capabilities: new[] { AuthorityPluginCapabilities.Password },
Metadata: new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase),
ConfigPath: string.Empty);
var context = new AuthorityPluginContext(manifest, configuration);
var services = new ServiceCollection();
services.AddScoped<IIdentityProviderPlugin>(_ => new ScopedIdentityProviderPlugin(context));
using var serviceProvider = services.BuildServiceProvider();
var registry = new AuthorityIdentityProviderRegistry(serviceProvider, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
await using var first = await registry.AcquireAsync("scoped", default);
await using var second = await registry.AcquireAsync("scoped", default);
var firstPlugin = Assert.IsType<ScopedIdentityProviderPlugin>(first.Provider);
var secondPlugin = Assert.IsType<ScopedIdentityProviderPlugin>(second.Provider);
Assert.NotEqual(firstPlugin.InstanceId, secondPlugin.InstanceId);
}
private static ServiceProvider BuildServiceProvider(IEnumerable<IIdentityProviderPlugin> providers)
{
var services = new ServiceCollection();
foreach (var provider in providers)
{
services.AddSingleton<IIdentityProviderPlugin>(provider);
}
return services.BuildServiceProvider();
}
private static IIdentityProviderPlugin CreateProvider(
@@ -122,4 +175,36 @@ public class AuthorityIdentityProviderRegistryTests
public ValueTask<AuthorityPluginHealthResult> CheckHealthAsync(CancellationToken cancellationToken)
=> ValueTask.FromResult(AuthorityPluginHealthResult.Healthy());
}
private sealed class ScopedIdentityProviderPlugin : IIdentityProviderPlugin
{
public ScopedIdentityProviderPlugin(AuthorityPluginContext context)
{
Context = context;
InstanceId = Guid.NewGuid();
Capabilities = new AuthorityIdentityProviderCapabilities(
SupportsPassword: true,
SupportsMfa: false,
SupportsClientProvisioning: false);
}
public Guid InstanceId { get; }
public string Name => Context.Manifest.Name;
public string Type => Context.Manifest.Type;
public AuthorityPluginContext Context { get; }
public IUserCredentialStore Credentials => throw new NotImplementedException();
public IClaimsEnricher ClaimsEnricher => throw new NotImplementedException();
public IClientProvisioningStore? ClientProvisioning => null;
public AuthorityIdentityProviderCapabilities Capabilities { get; }
public ValueTask<AuthorityPluginHealthResult> CheckHealthAsync(CancellationToken cancellationToken)
=> ValueTask.FromResult(AuthorityPluginHealthResult.Healthy());
}
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using StellaOps.Authority.OpenIddict;
using StellaOps.Authority.Plugins.Abstractions;
@@ -67,8 +68,14 @@ public class AuthorityIdentityProviderSelectorTests
private static AuthorityIdentityProviderRegistry CreateRegistry(IEnumerable<IIdentityProviderPlugin> passwordProviders)
{
var providers = passwordProviders.ToList<IIdentityProviderPlugin>();
return new AuthorityIdentityProviderRegistry(providers, Microsoft.Extensions.Logging.Abstractions.NullLogger<AuthorityIdentityProviderRegistry>.Instance);
var services = new ServiceCollection();
foreach (var provider in passwordProviders)
{
services.AddSingleton<IIdentityProviderPlugin>(provider);
}
var serviceProvider = services.BuildServiceProvider();
return new AuthorityIdentityProviderRegistry(serviceProvider, Microsoft.Extensions.Logging.Abstractions.NullLogger<AuthorityIdentityProviderRegistry>.Instance);
}
private static IIdentityProviderPlugin CreateProvider(string name, bool supportsPassword)

View File

@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
@@ -350,6 +351,7 @@ public class ClientCredentialsHandlersTests
};
options.Security.SenderConstraints.Mtls.Enabled = true;
options.Security.SenderConstraints.Mtls.RequireChainValidation = false;
options.Security.SenderConstraints.Mtls.AllowedSanTypes.Clear();
options.Signing.ActiveKeyId = "test-key";
options.Signing.KeyPath = "/tmp/test-key.pem";
options.Storage.ConnectionString = "mongodb://localhost/test";
@@ -394,7 +396,7 @@ public class ClientCredentialsHandlersTests
await handler.HandleAsync(context);
Assert.False(context.IsRejected);
Assert.False(context.IsRejected, context.ErrorDescription ?? context.Error);
Assert.Equal(AuthoritySenderConstraintKinds.Mtls, context.Transaction.Properties[AuthorityOpenIddictConstants.SenderConstraintProperty]);
var expectedBase64 = Base64UrlEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256));
@@ -581,7 +583,7 @@ public class TokenValidationHandlersTests
descriptor: CreateDescriptor(clientDocument),
user: userDescriptor);
var registry = new AuthorityIdentityProviderRegistry(new[] { plugin }, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
var registry = CreateRegistryFromPlugins(plugin);
var metadataAccessorSuccess = new TestRateLimiterMetadataAccessor();
var auditSinkSuccess = new TestAuthEventSink();
@@ -1073,7 +1075,7 @@ internal static class TestHelpers
descriptor: clientDescriptor,
user: null);
return new AuthorityIdentityProviderRegistry(new[] { plugin }, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
return CreateRegistryFromPlugins(plugin);
}
public static TestIdentityProviderPlugin CreatePlugin(
@@ -1109,6 +1111,19 @@ internal static class TestHelpers
SupportsClientProvisioning: supportsClientProvisioning));
}
public static AuthorityIdentityProviderRegistry CreateRegistryFromPlugins(params IIdentityProviderPlugin[] plugins)
{
var services = new ServiceCollection();
services.AddLogging();
foreach (var plugin in plugins)
{
services.AddSingleton<IIdentityProviderPlugin>(plugin);
}
var provider = services.BuildServiceProvider();
return new AuthorityIdentityProviderRegistry(provider, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
}
public static OpenIddictServerTransaction CreateTokenTransaction(string clientId, string? secret, string? scope)
{
var request = new OpenIddictRequest

View File

@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Server;
using OpenIddict.Server.AspNetCore;
@@ -97,7 +98,13 @@ public class PasswordGrantHandlersTests
private static AuthorityIdentityProviderRegistry CreateRegistry(IUserCredentialStore store)
{
var plugin = new StubIdentityProviderPlugin("stub", store);
return new AuthorityIdentityProviderRegistry(new[] { plugin }, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton<IIdentityProviderPlugin>(plugin);
var provider = services.BuildServiceProvider();
return new AuthorityIdentityProviderRegistry(provider, NullLogger<AuthorityIdentityProviderRegistry>.Instance);
}
private static OpenIddictServerTransaction CreatePasswordTransaction(string username, string password)

View File

@@ -131,9 +131,7 @@ public sealed class TokenPersistenceIntegrationTests
descriptor,
userDescriptor);
var registry = new AuthorityIdentityProviderRegistry(
new[] { plugin },
NullLogger<AuthorityIdentityProviderRegistry>.Instance);
var registry = TestHelpers.CreateRegistryFromPlugins(plugin);
const string revokedTokenId = "refresh-token-1";
var refreshToken = new AuthorityTokenDocument

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Authority.Plugins;
using StellaOps.Authority.Plugins.Abstractions;
@@ -67,6 +68,7 @@ public class AuthorityPluginLoaderTests
public void RegisterPlugins_RegistersEnabledPlugin_WhenRegistrarAvailable()
{
var services = new ServiceCollection();
services.AddLogging();
var hostConfiguration = new ConfigurationBuilder().Build();
var manifest = new AuthorityPluginManifest(
@@ -99,6 +101,46 @@ public class AuthorityPluginLoaderTests
Assert.NotNull(provider.GetRequiredService<TestMarkerService>());
}
[Fact]
public void RegisterPlugins_ActivatesRegistrarUsingDependencyInjection()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton(TimeProvider.System);
var hostConfiguration = new ConfigurationBuilder().Build();
var manifest = new AuthorityPluginManifest(
"di-test",
DiAuthorityPluginRegistrar.PluginTypeIdentifier,
true,
typeof(DiAuthorityPluginRegistrar).Assembly.GetName().Name,
typeof(DiAuthorityPluginRegistrar).Assembly.Location,
Array.Empty<string>(),
new Dictionary<string, string?>(),
"di-test.yaml");
var pluginContext = new AuthorityPluginContext(manifest, hostConfiguration);
var descriptor = new AuthorityPluginLoader.LoadedPluginDescriptor(
typeof(DiAuthorityPluginRegistrar).Assembly,
typeof(DiAuthorityPluginRegistrar).Assembly.Location);
var summary = AuthorityPluginLoader.RegisterPluginsCore(
services,
hostConfiguration,
new[] { pluginContext },
new[] { descriptor },
Array.Empty<string>(),
NullLogger.Instance);
Assert.Contains("di-test", summary.RegisteredPlugins);
var provider = services.BuildServiceProvider();
var dependent = provider.GetRequiredService<DependentService>();
Assert.True(dependent.LoggerWasResolved);
Assert.True(dependent.TimeProviderResolved);
}
private sealed class TestAuthorityPluginRegistrar : IAuthorityPluginRegistrar
{
public const string PluginTypeIdentifier = "test-plugin";
@@ -114,4 +156,38 @@ public class AuthorityPluginLoaderTests
private sealed class TestMarkerService
{
}
private sealed class DiAuthorityPluginRegistrar : IAuthorityPluginRegistrar
{
public const string PluginTypeIdentifier = "test-plugin-di";
private readonly ILogger<DiAuthorityPluginRegistrar> logger;
private readonly TimeProvider timeProvider;
public DiAuthorityPluginRegistrar(ILogger<DiAuthorityPluginRegistrar> logger, TimeProvider timeProvider)
{
this.logger = logger;
this.timeProvider = timeProvider;
}
public string PluginType => PluginTypeIdentifier;
public void Register(AuthorityPluginRegistrationContext context)
{
context.Services.AddSingleton(new DependentService(logger != null, timeProvider != null));
}
}
private sealed class DependentService
{
public DependentService(bool loggerResolved, bool timeProviderResolved)
{
LoggerWasResolved = loggerResolved;
TimeProviderResolved = timeProviderResolved;
}
public bool LoggerWasResolved { get; }
public bool TimeProviderResolved { get; }
}
}

View File

@@ -1,5 +1,9 @@
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Authority.Plugins.Abstractions;
@@ -7,29 +11,34 @@ namespace StellaOps.Authority;
internal sealed class AuthorityIdentityProviderRegistry : IAuthorityIdentityProviderRegistry
{
private readonly IReadOnlyDictionary<string, IIdentityProviderPlugin> providersByName;
private readonly ReadOnlyCollection<IIdentityProviderPlugin> providers;
private readonly ReadOnlyCollection<IIdentityProviderPlugin> passwordProviders;
private readonly ReadOnlyCollection<IIdentityProviderPlugin> mfaProviders;
private readonly ReadOnlyCollection<IIdentityProviderPlugin> clientProvisioningProviders;
private readonly IServiceProvider serviceProvider;
private readonly IReadOnlyDictionary<string, AuthorityIdentityProviderMetadata> providersByName;
private readonly ReadOnlyCollection<AuthorityIdentityProviderMetadata> providers;
private readonly ReadOnlyCollection<AuthorityIdentityProviderMetadata> passwordProviders;
private readonly ReadOnlyCollection<AuthorityIdentityProviderMetadata> mfaProviders;
private readonly ReadOnlyCollection<AuthorityIdentityProviderMetadata> clientProvisioningProviders;
public AuthorityIdentityProviderRegistry(
IEnumerable<IIdentityProviderPlugin> providerInstances,
IServiceProvider serviceProvider,
ILogger<AuthorityIdentityProviderRegistry> logger)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
logger = logger ?? throw new ArgumentNullException(nameof(logger));
using var scope = serviceProvider.CreateScope();
var providerInstances = scope.ServiceProvider.GetServices<IIdentityProviderPlugin>();
var orderedProviders = providerInstances?
.Where(static p => p is not null)
.OrderBy(static p => p.Name, StringComparer.OrdinalIgnoreCase)
.ToList() ?? new List<IIdentityProviderPlugin>();
var uniqueProviders = new List<IIdentityProviderPlugin>(orderedProviders.Count);
var password = new List<IIdentityProviderPlugin>();
var mfa = new List<IIdentityProviderPlugin>();
var clientProvisioning = new List<IIdentityProviderPlugin>();
var uniqueProviders = new List<AuthorityIdentityProviderMetadata>(orderedProviders.Count);
var password = new List<AuthorityIdentityProviderMetadata>();
var mfa = new List<AuthorityIdentityProviderMetadata>();
var clientProvisioning = new List<AuthorityIdentityProviderMetadata>();
var dictionary = new Dictionary<string, IIdentityProviderPlugin>(StringComparer.OrdinalIgnoreCase);
var dictionary = new Dictionary<string, AuthorityIdentityProviderMetadata>(StringComparer.OrdinalIgnoreCase);
foreach (var provider in orderedProviders)
{
@@ -41,7 +50,9 @@ internal sealed class AuthorityIdentityProviderRegistry : IAuthorityIdentityProv
continue;
}
if (!dictionary.TryAdd(provider.Name, provider))
var metadata = new AuthorityIdentityProviderMetadata(provider.Name, provider.Type, provider.Capabilities);
if (!dictionary.TryAdd(provider.Name, metadata))
{
logger.LogWarning(
"Duplicate identity provider name '{PluginName}' detected; ignoring additional registration for type '{PluginType}'.",
@@ -50,29 +61,29 @@ internal sealed class AuthorityIdentityProviderRegistry : IAuthorityIdentityProv
continue;
}
uniqueProviders.Add(provider);
uniqueProviders.Add(metadata);
if (provider.Capabilities.SupportsPassword)
if (metadata.Capabilities.SupportsPassword)
{
password.Add(provider);
password.Add(metadata);
}
if (provider.Capabilities.SupportsMfa)
if (metadata.Capabilities.SupportsMfa)
{
mfa.Add(provider);
mfa.Add(metadata);
}
if (provider.Capabilities.SupportsClientProvisioning)
if (metadata.Capabilities.SupportsClientProvisioning)
{
clientProvisioning.Add(provider);
clientProvisioning.Add(metadata);
}
}
providersByName = dictionary;
providers = new ReadOnlyCollection<IIdentityProviderPlugin>(uniqueProviders);
passwordProviders = new ReadOnlyCollection<IIdentityProviderPlugin>(password);
mfaProviders = new ReadOnlyCollection<IIdentityProviderPlugin>(mfa);
clientProvisioningProviders = new ReadOnlyCollection<IIdentityProviderPlugin>(clientProvisioning);
providers = new ReadOnlyCollection<AuthorityIdentityProviderMetadata>(uniqueProviders);
passwordProviders = new ReadOnlyCollection<AuthorityIdentityProviderMetadata>(password);
mfaProviders = new ReadOnlyCollection<AuthorityIdentityProviderMetadata>(mfa);
clientProvisioningProviders = new ReadOnlyCollection<AuthorityIdentityProviderMetadata>(clientProvisioning);
AggregateCapabilities = new AuthorityIdentityProviderCapabilities(
SupportsPassword: passwordProviders.Count > 0,
@@ -80,24 +91,56 @@ internal sealed class AuthorityIdentityProviderRegistry : IAuthorityIdentityProv
SupportsClientProvisioning: clientProvisioningProviders.Count > 0);
}
public IReadOnlyCollection<IIdentityProviderPlugin> Providers => providers;
public IReadOnlyCollection<AuthorityIdentityProviderMetadata> Providers => providers;
public IReadOnlyCollection<IIdentityProviderPlugin> PasswordProviders => passwordProviders;
public IReadOnlyCollection<AuthorityIdentityProviderMetadata> PasswordProviders => passwordProviders;
public IReadOnlyCollection<IIdentityProviderPlugin> MfaProviders => mfaProviders;
public IReadOnlyCollection<AuthorityIdentityProviderMetadata> MfaProviders => mfaProviders;
public IReadOnlyCollection<IIdentityProviderPlugin> ClientProvisioningProviders => clientProvisioningProviders;
public IReadOnlyCollection<AuthorityIdentityProviderMetadata> ClientProvisioningProviders => clientProvisioningProviders;
public AuthorityIdentityProviderCapabilities AggregateCapabilities { get; }
public bool TryGet(string name, [NotNullWhen(true)] out IIdentityProviderPlugin? provider)
public bool TryGet(string name, [NotNullWhen(true)] out AuthorityIdentityProviderMetadata? metadata)
{
if (string.IsNullOrWhiteSpace(name))
{
provider = null;
metadata = null;
return false;
}
return providersByName.TryGetValue(name, out provider);
return providersByName.TryGetValue(name, out metadata);
}
public async ValueTask<AuthorityIdentityProviderHandle> AcquireAsync(string name, CancellationToken cancellationToken)
{
if (!providersByName.TryGetValue(name, out var metadata))
{
throw new KeyNotFoundException($"Identity provider plugin '{name}' is not registered.");
}
cancellationToken.ThrowIfCancellationRequested();
var scope = serviceProvider.CreateAsyncScope();
try
{
var provider = scope.ServiceProvider
.GetServices<IIdentityProviderPlugin>()
.FirstOrDefault(p => string.Equals(p.Name, metadata.Name, StringComparison.OrdinalIgnoreCase));
if (provider is null)
{
await scope.DisposeAsync().ConfigureAwait(false);
throw new InvalidOperationException($"Identity provider plugin '{metadata.Name}' could not be resolved.");
}
cancellationToken.ThrowIfCancellationRequested();
return new AuthorityIdentityProviderHandle(scope, metadata, provider);
}
catch
{
await scope.DisposeAsync().ConfigureAwait(false);
throw;
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Linq;
using OpenIddict.Abstractions;
using StellaOps.Authority.Plugins.Abstractions;
@@ -50,11 +51,11 @@ internal static class AuthorityIdentityProviderSelector
internal sealed record ProviderSelectionResult(
bool Succeeded,
IIdentityProviderPlugin? Provider,
AuthorityIdentityProviderMetadata? Provider,
string? Error,
string? Description)
{
public static ProviderSelectionResult Success(IIdentityProviderPlugin provider)
public static ProviderSelectionResult Success(AuthorityIdentityProviderMetadata provider)
=> new(true, provider, null, null);
public static ProviderSelectionResult Failure(string error, string description)

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
@@ -159,25 +159,28 @@ internal sealed class ValidateClientCredentialsHandler : IOpenIddictServerHandle
context.Transaction.Properties[AuthorityOpenIddictConstants.AuditConfidentialProperty] =
string.Equals(document.ClientType, "confidential", StringComparison.OrdinalIgnoreCase);
IIdentityProviderPlugin? provider = null;
if (!string.IsNullOrWhiteSpace(document.Plugin))
{
if (!registry.TryGet(document.Plugin, out provider))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Configured identity provider is unavailable.");
logger.LogWarning("Client credentials validation failed for {ClientId}: provider {Provider} unavailable.", context.ClientId, document.Plugin);
return;
}
context.Transaction.Properties[AuthorityOpenIddictConstants.AuditProviderProperty] = provider.Name;
if (!provider.Capabilities.SupportsClientProvisioning || provider.ClientProvisioning is null)
{
context.Reject(OpenIddictConstants.Errors.UnauthorizedClient, "Associated identity provider does not support client provisioning.");
logger.LogWarning("Client credentials validation failed for {ClientId}: provider {Provider} lacks client provisioning capabilities.", context.ClientId, provider.Name);
return;
}
}
AuthorityIdentityProviderMetadata? providerMetadata = null;
if (!string.IsNullOrWhiteSpace(document.Plugin))
{
if (!registry.TryGet(document.Plugin, out providerMetadata))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Configured identity provider is unavailable.");
logger.LogWarning("Client credentials validation failed for {ClientId}: provider {Provider} unavailable.", context.ClientId, document.Plugin);
return;
}
await using var providerHandle = await registry.AcquireAsync(providerMetadata.Name, context.CancellationToken).ConfigureAwait(false);
var providerInstance = providerHandle.Provider;
context.Transaction.Properties[AuthorityOpenIddictConstants.AuditProviderProperty] = providerMetadata.Name;
if (!providerMetadata.Capabilities.SupportsClientProvisioning || providerInstance.ClientProvisioning is null)
{
context.Reject(OpenIddictConstants.Errors.UnauthorizedClient, "Associated identity provider does not support client provisioning.");
logger.LogWarning("Client credentials validation failed for {ClientId}: provider {Provider} lacks client provisioning capabilities.", context.ClientId, providerMetadata.Name);
return;
}
}
var allowedGrantTypes = ClientCredentialHandlerHelpers.Split(document.Properties, AuthorityClientMetadataKeys.AllowedGrantTypes);
if (allowedGrantTypes.Count > 0 &&
@@ -191,28 +194,28 @@ internal sealed class ValidateClientCredentialsHandler : IOpenIddictServerHandle
var requiresSecret = string.Equals(document.ClientType, "confidential", StringComparison.OrdinalIgnoreCase);
if (requiresSecret)
{
if (string.IsNullOrWhiteSpace(document.SecretHash))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Client secret is not configured.");
logger.LogWarning("Client credentials validation failed for {ClientId}: secret not configured.", document.ClientId);
return;
}
if (string.IsNullOrWhiteSpace(context.ClientSecret) ||
!ClientCredentialHandlerHelpers.VerifySecret(context.ClientSecret, document.SecretHash))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Invalid client credentials.");
logger.LogWarning("Client credentials validation failed for {ClientId}: secret verification failed.", document.ClientId);
return;
}
}
else if (!string.IsNullOrWhiteSpace(context.ClientSecret) && !string.IsNullOrWhiteSpace(document.SecretHash) &&
!ClientCredentialHandlerHelpers.VerifySecret(context.ClientSecret, document.SecretHash))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Invalid client credentials.");
logger.LogWarning("Client credentials validation failed for {ClientId}: secret verification failed.", document.ClientId);
return;
}
if (string.IsNullOrWhiteSpace(document.SecretHash))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Client secret is not configured.");
logger.LogWarning("Client credentials validation failed for {ClientId}: secret not configured.", document.ClientId);
return;
}
if (string.IsNullOrWhiteSpace(context.ClientSecret) ||
!ClientCredentialHandlerHelpers.VerifySecret(context.ClientSecret, document.SecretHash))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Invalid client credentials.");
logger.LogWarning("Client credentials validation failed for {ClientId}: secret verification failed.", document.ClientId);
return;
}
}
else if (!string.IsNullOrWhiteSpace(context.ClientSecret) && !string.IsNullOrWhiteSpace(document.SecretHash) &&
!ClientCredentialHandlerHelpers.VerifySecret(context.ClientSecret, document.SecretHash))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Invalid client credentials.");
logger.LogWarning("Client credentials validation failed for {ClientId}: secret verification failed.", document.ClientId);
return;
}
var allowedScopes = ClientCredentialHandlerHelpers.Split(document.Properties, AuthorityClientMetadataKeys.AllowedScopes);
var resolvedScopes = ClientCredentialHandlerHelpers.ResolveGrantedScopes(
@@ -230,11 +233,11 @@ internal sealed class ValidateClientCredentialsHandler : IOpenIddictServerHandle
context.Transaction.Properties[AuthorityOpenIddictConstants.AuditGrantedScopesProperty] = resolvedScopes.Scopes;
context.Transaction.Properties[AuthorityOpenIddictConstants.ClientTransactionProperty] = document;
if (provider is not null)
{
context.Transaction.Properties[AuthorityOpenIddictConstants.ClientProviderTransactionProperty] = provider.Name;
activity?.SetTag("authority.identity_provider", provider.Name);
}
if (providerMetadata is not null)
{
context.Transaction.Properties[AuthorityOpenIddictConstants.ClientProviderTransactionProperty] = providerMetadata.Name;
activity?.SetTag("authority.identity_provider", providerMetadata.Name);
}
context.Transaction.Properties[AuthorityOpenIddictConstants.ClientGrantedScopesProperty] = resolvedScopes.Scopes;
logger.LogInformation("Client credentials validated for {ClientId}.", document.ClientId);
@@ -373,70 +376,88 @@ internal sealed class HandleClientCredentialsHandler : IOpenIddictServerHandler<
_ => new[] { OpenIddictConstants.Destinations.AccessToken }
});
var (provider, descriptor) = await ResolveProviderAsync(context, document).ConfigureAwait(false);
if (context.IsRejected)
{
logger.LogWarning("Client credentials request rejected for {ClientId} during provider resolution.", document.ClientId);
return;
}
if (provider is null)
{
if (!string.IsNullOrWhiteSpace(document.Plugin))
{
identity.SetClaim(StellaOpsClaimTypes.IdentityProvider, document.Plugin);
activity?.SetTag("authority.identity_provider", document.Plugin);
}
}
else
{
identity.SetClaim(StellaOpsClaimTypes.IdentityProvider, provider.Name);
activity?.SetTag("authority.identity_provider", provider.Name);
}
ApplySenderConstraintClaims(context, identity, document);
var principal = new ClaimsPrincipal(identity);
var grantedScopes = context.Transaction.Properties.TryGetValue(AuthorityOpenIddictConstants.ClientGrantedScopesProperty, out var scopesValue) &&
scopesValue is IReadOnlyList<string> resolvedScopes
? resolvedScopes
: ClientCredentialHandlerHelpers.Split(document.Properties, AuthorityClientMetadataKeys.AllowedScopes);
if (grantedScopes.Count > 0)
{
principal.SetScopes(grantedScopes);
}
else
{
principal.SetScopes(Array.Empty<string>());
}
if (configuredAudiences.Count > 0)
{
principal.SetAudiences(configuredAudiences);
}
if (provider is not null && descriptor is not null)
{
var enrichmentContext = new AuthorityClaimsEnrichmentContext(provider.Context, user: null, descriptor);
await provider.ClaimsEnricher.EnrichAsync(identity, enrichmentContext, context.CancellationToken).ConfigureAwait(false);
}
var session = await sessionAccessor.GetSessionAsync(context.CancellationToken).ConfigureAwait(false);
await PersistTokenAsync(context, document, tokenId, grantedScopes, session, activity).ConfigureAwait(false);
context.Principal = principal;
context.HandleRequest();
logger.LogInformation("Issued client credentials access token for {ClientId} with scopes {Scopes}.", document.ClientId, grantedScopes);
}
private async ValueTask<(IIdentityProviderPlugin? Provider, AuthorityClientDescriptor? Descriptor)> ResolveProviderAsync(
OpenIddictServerEvents.HandleTokenRequestContext context,
AuthorityClientDocument document)
{
string? providerName = null;
if (context.Transaction.Properties.TryGetValue(AuthorityOpenIddictConstants.ClientProviderTransactionProperty, out var providerValue) &&
var (providerHandle, descriptor) = await ResolveProviderAsync(context, document).ConfigureAwait(false);
if (context.IsRejected)
{
if (providerHandle is not null)
{
await providerHandle.DisposeAsync().ConfigureAwait(false);
}
logger.LogWarning("Client credentials request rejected for {ClientId} during provider resolution.", document.ClientId);
return;
}
AuthorityIdentityProviderHandle? handle = providerHandle;
try
{
var provider = handle?.Provider;
if (provider is null)
{
if (!string.IsNullOrWhiteSpace(document.Plugin))
{
identity.SetClaim(StellaOpsClaimTypes.IdentityProvider, document.Plugin);
activity?.SetTag("authority.identity_provider", document.Plugin);
}
}
else
{
identity.SetClaim(StellaOpsClaimTypes.IdentityProvider, provider.Name);
activity?.SetTag("authority.identity_provider", provider.Name);
}
ApplySenderConstraintClaims(context, identity, document);
var principal = new ClaimsPrincipal(identity);
var grantedScopes = context.Transaction.Properties.TryGetValue(AuthorityOpenIddictConstants.ClientGrantedScopesProperty, out var scopesValue) &&
scopesValue is IReadOnlyList<string> resolvedScopes
? resolvedScopes
: ClientCredentialHandlerHelpers.Split(document.Properties, AuthorityClientMetadataKeys.AllowedScopes);
if (grantedScopes.Count > 0)
{
principal.SetScopes(grantedScopes);
}
else
{
principal.SetScopes(Array.Empty<string>());
}
if (configuredAudiences.Count > 0)
{
principal.SetAudiences(configuredAudiences);
}
if (provider is not null && descriptor is not null)
{
var enrichmentContext = new AuthorityClaimsEnrichmentContext(provider.Context, user: null, descriptor);
await provider.ClaimsEnricher.EnrichAsync(identity, enrichmentContext, context.CancellationToken).ConfigureAwait(false);
}
var session = await sessionAccessor.GetSessionAsync(context.CancellationToken).ConfigureAwait(false);
await PersistTokenAsync(context, document, tokenId, grantedScopes, session, activity).ConfigureAwait(false);
context.Principal = principal;
context.HandleRequest();
logger.LogInformation("Issued client credentials access token for {ClientId} with scopes {Scopes}.", document.ClientId, grantedScopes);
}
finally
{
if (handle is not null)
{
await handle.DisposeAsync().ConfigureAwait(false);
}
}
}
private async ValueTask<(AuthorityIdentityProviderHandle? Handle, AuthorityClientDescriptor? Descriptor)> ResolveProviderAsync(
OpenIddictServerEvents.HandleTokenRequestContext context,
AuthorityClientDocument document)
{
string? providerName = null;
if (context.Transaction.Properties.TryGetValue(AuthorityOpenIddictConstants.ClientProviderTransactionProperty, out var providerValue) &&
providerValue is string storedProvider)
{
providerName = storedProvider;
@@ -446,27 +467,46 @@ internal sealed class HandleClientCredentialsHandler : IOpenIddictServerHandler<
providerName = document.Plugin;
}
if (string.IsNullOrWhiteSpace(providerName))
{
return (null, null);
}
if (!registry.TryGet(providerName, out var provider) || provider.ClientProvisioning is null)
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Configured identity provider is unavailable.");
return (null, null);
}
var descriptor = await provider.ClientProvisioning.FindByClientIdAsync(document.ClientId, context.CancellationToken).ConfigureAwait(false);
if (descriptor is null)
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Client registration was not found.");
return (null, null);
}
return (provider, descriptor);
}
if (string.IsNullOrWhiteSpace(providerName))
{
return (null, null);
}
if (!registry.TryGet(providerName, out var metadata))
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Configured identity provider is unavailable.");
return (null, null);
}
var handle = await registry.AcquireAsync(metadata.Name, context.CancellationToken).ConfigureAwait(false);
try
{
var provider = handle.Provider;
if (provider.ClientProvisioning is null)
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Associated identity provider does not support client provisioning.");
await handle.DisposeAsync().ConfigureAwait(false);
return (null, null);
}
var descriptor = await provider.ClientProvisioning.FindByClientIdAsync(document.ClientId, context.CancellationToken).ConfigureAwait(false);
if (descriptor is null)
{
context.Reject(OpenIddictConstants.Errors.InvalidClient, "Client registration was not found.");
await handle.DisposeAsync().ConfigureAwait(false);
return (null, null);
}
return (handle, descriptor);
}
catch
{
await handle.DisposeAsync().ConfigureAwait(false);
throw;
}
}
private async ValueTask PersistTokenAsync(
OpenIddictServerEvents.HandleTokenRequestContext context,

View File

@@ -367,66 +367,79 @@ internal sealed class ValidateDpopProofHandler : IOpenIddictServerHandler<OpenId
return new Uri(url, UriKind.Absolute);
}
private static string? ResolveNonceAudience(OpenIddictRequest request, AuthorityDpopNonceOptions nonceOptions, IReadOnlyList<string> configuredAudiences)
{
if (!nonceOptions.Enabled || request is null)
{
return null;
}
if (request.Resources is not null)
{
foreach (var resource in request.Resources)
{
if (string.IsNullOrWhiteSpace(resource))
{
continue;
}
var normalized = resource.Trim();
if (nonceOptions.RequiredAudiences.Contains(normalized))
{
return normalized;
}
}
}
if (request.Audiences is not null)
{
foreach (var audience in request.Audiences)
{
if (string.IsNullOrWhiteSpace(audience))
{
continue;
}
var normalized = audience.Trim();
if (nonceOptions.RequiredAudiences.Contains(normalized))
{
return normalized;
}
}
}
if (configuredAudiences is { Count: > 0 })
{
foreach (var audience in configuredAudiences)
{
if (string.IsNullOrWhiteSpace(audience))
{
continue;
}
var normalized = audience.Trim();
if (nonceOptions.RequiredAudiences.Contains(normalized))
{
return normalized;
}
}
}
return null;
}
private static string? ResolveNonceAudience(
OpenIddictRequest request,
AuthorityDpopNonceOptions nonceOptions,
IReadOnlyList<string> configuredAudiences)
{
if (!nonceOptions.Enabled || request is null)
{
return null;
}
var normalizedAudiences = nonceOptions.NormalizedAudiences;
IReadOnlySet<string> effectiveAudiences;
if (normalizedAudiences.Count > 0)
{
effectiveAudiences = normalizedAudiences;
}
else if (nonceOptions.RequiredAudiences.Count > 0)
{
effectiveAudiences = nonceOptions.RequiredAudiences.ToHashSet(StringComparer.OrdinalIgnoreCase);
}
else
{
return null;
}
bool TryMatch(string? candidate, out string normalized)
{
normalized = string.Empty;
if (string.IsNullOrWhiteSpace(candidate))
{
return false;
}
normalized = candidate.Trim();
return effectiveAudiences.Contains(normalized);
}
if (request.Resources is not null)
{
foreach (var resource in request.Resources)
{
if (TryMatch(resource, out var normalized))
{
return normalized;
}
}
}
if (request.Audiences is not null)
{
foreach (var audience in request.Audiences)
{
if (TryMatch(audience, out var normalized))
{
return normalized;
}
}
}
if (configuredAudiences is { Count: > 0 })
{
foreach (var audience in configuredAudiences)
{
if (TryMatch(audience, out var normalized))
{
return normalized;
}
}
}
return null;
}
private async ValueTask ChallengeNonceAsync(
OpenIddictServerEvents.ValidateTokenRequestContext context,

View File

@@ -110,6 +110,8 @@ internal sealed class ValidatePasswordGrantHandler : IOpenIddictServerHandler<Op
return;
}
var selectedProvider = selection.Provider!;
if (string.IsNullOrWhiteSpace(context.Request.Username) || string.IsNullOrEmpty(context.Request.Password))
{
var record = PasswordGrantAuditHelper.CreatePasswordGrantRecord(
@@ -119,7 +121,7 @@ internal sealed class ValidatePasswordGrantHandler : IOpenIddictServerHandler<Op
AuthEventOutcome.Failure,
"Both username and password must be provided.",
clientId,
providerName: selection.Provider?.Name,
providerName: selectedProvider.Name,
user: null,
username: context.Request.Username,
scopes: requestedScopes,
@@ -134,9 +136,9 @@ internal sealed class ValidatePasswordGrantHandler : IOpenIddictServerHandler<Op
return;
}
context.Transaction.Properties[AuthorityOpenIddictConstants.ProviderTransactionProperty] = selection.Provider!.Name;
activity?.SetTag("authority.identity_provider", selection.Provider.Name);
logger.LogInformation("Password grant validation succeeded for {Username} using provider {Provider}.", context.Request.Username, selection.Provider.Name);
context.Transaction.Properties[AuthorityOpenIddictConstants.ProviderTransactionProperty] = selectedProvider.Name;
activity?.SetTag("authority.identity_provider", selectedProvider.Name);
logger.LogInformation("Password grant validation succeeded for {Username} using provider {Provider}.", context.Request.Username, selectedProvider.Name);
}
}
@@ -195,10 +197,10 @@ internal sealed class HandlePasswordGrantHandler : IOpenIddictServerHandler<Open
? value as string
: null;
IIdentityProviderPlugin? resolvedProvider;
AuthorityIdentityProviderMetadata? providerMetadata = null;
if (!string.IsNullOrWhiteSpace(providerName))
{
if (!registry.TryGet(providerName!, out var explicitProvider))
if (!registry.TryGet(providerName!, out providerMetadata))
{
var record = PasswordGrantAuditHelper.CreatePasswordGrantRecord(
timeProvider,
@@ -221,8 +223,6 @@ internal sealed class HandlePasswordGrantHandler : IOpenIddictServerHandler<Open
logger.LogError("Password grant handling failed: provider {Provider} not found for user {Username}.", providerName, context.Request.Username);
return;
}
resolvedProvider = explicitProvider;
}
else
{
@@ -251,11 +251,17 @@ internal sealed class HandlePasswordGrantHandler : IOpenIddictServerHandler<Open
return;
}
resolvedProvider = selection.Provider;
providerName = selection.Provider?.Name;
providerMetadata = selection.Provider;
providerName = providerMetadata?.Name;
}
var provider = resolvedProvider ?? throw new InvalidOperationException("No identity provider resolved for password grant.");
if (providerMetadata is null)
{
throw new InvalidOperationException("No identity provider metadata resolved for password grant.");
}
await using var providerHandle = await registry.AcquireAsync(providerMetadata.Name, context.CancellationToken).ConfigureAwait(false);
var provider = providerHandle.Provider;
var username = context.Request.Username;
var password = context.Request.Password;
@@ -268,7 +274,7 @@ internal sealed class HandlePasswordGrantHandler : IOpenIddictServerHandler<Open
AuthEventOutcome.Failure,
"Both username and password must be provided.",
clientId,
provider.Name,
providerMetadata.Name,
user: null,
username: username,
scopes: requestedScopes,
@@ -301,7 +307,7 @@ internal sealed class HandlePasswordGrantHandler : IOpenIddictServerHandler<Open
outcome,
verification.Message,
clientId,
provider.Name,
providerMetadata.Name,
verification.User,
username,
scopes: requestedScopes,
@@ -360,7 +366,7 @@ internal sealed class HandlePasswordGrantHandler : IOpenIddictServerHandler<Open
AuthEventOutcome.Success,
verification.Message,
clientId,
provider.Name,
providerMetadata.Name,
verification.User,
username,
scopes: requestedScopes,

View File

@@ -141,13 +141,16 @@ internal sealed class ValidateAccessTokenHandler : IOpenIddictServerHandler<Open
return;
}
if (!registry.TryGet(providerName, out var provider))
if (!registry.TryGet(providerName, out var providerMetadata))
{
context.Reject(OpenIddictConstants.Errors.InvalidToken, "The identity provider associated with the token is unavailable.");
logger.LogWarning("Access token validation failed: provider {Provider} unavailable for subject {Subject}.", providerName, context.Principal.GetClaim(OpenIddictConstants.Claims.Subject));
return;
}
await using var providerHandle = await registry.AcquireAsync(providerMetadata.Name, context.CancellationToken).ConfigureAwait(false);
var provider = providerHandle.Provider;
AuthorityUserDescriptor? user = null;
AuthorityClientDescriptor? client = null;

View File

@@ -6,6 +6,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Authority.Plugins.Abstractions;
using StellaOps.Plugin.DependencyInjection;
using StellaOps.Plugin.Hosting;
namespace StellaOps.Authority.Plugins;
@@ -51,7 +52,9 @@ internal static class AuthorityPluginLoader
IReadOnlyCollection<string> missingOrdered,
ILogger? logger)
{
var registrarLookup = DiscoverRegistrars(loadedAssemblies, logger);
var registrarCandidates = DiscoverRegistrars(loadedAssemblies);
var pluginTypeLookup = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
var registrarTypeLookup = new Dictionary<Type, string>();
var registered = new List<string>();
var failures = new List<AuthorityPluginRegistrationFailure>();
@@ -79,7 +82,16 @@ internal static class AuthorityPluginLoader
continue;
}
if (!registrarLookup.TryGetValue(manifest.Type, out var registrar))
var activation = TryResolveActivationForManifest(
services,
manifest.Type,
registrarCandidates,
pluginTypeLookup,
registrarTypeLookup,
logger,
out var registrarType);
if (activation is null || registrarType is null)
{
var reason = $"No registrar found for plugin type '{manifest.Type}'.";
logger?.LogError(
@@ -92,7 +104,9 @@ internal static class AuthorityPluginLoader
try
{
registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
PluginServiceRegistration.RegisterAssemblyMetadata(services, registrarType.Assembly, logger);
activation.Registrar.Register(new AuthorityPluginRegistrationContext(services, pluginContext, configuration));
registered.Add(manifest.Name);
logger?.LogInformation(
@@ -109,6 +123,10 @@ internal static class AuthorityPluginLoader
manifest.Name);
failures.Add(new AuthorityPluginRegistrationFailure(manifest.Name, reason));
}
finally
{
activation.Dispose();
}
}
if (missingOrdered.Count > 0)
@@ -124,11 +142,9 @@ internal static class AuthorityPluginLoader
return new AuthorityPluginRegistrationSummary(registered, failures, missingOrdered);
}
private static Dictionary<string, IAuthorityPluginRegistrar> DiscoverRegistrars(
IReadOnlyCollection<LoadedPluginDescriptor> loadedAssemblies,
ILogger? logger)
private static IReadOnlyList<Type> DiscoverRegistrars(IReadOnlyCollection<LoadedPluginDescriptor> loadedAssemblies)
{
var lookup = new Dictionary<string, IAuthorityPluginRegistrar>(StringComparer.OrdinalIgnoreCase);
var registrars = new List<Type>();
foreach (var descriptor in loadedAssemblies)
{
@@ -139,43 +155,144 @@ internal static class AuthorityPluginLoader
continue;
}
try
{
if (Activator.CreateInstance(type) is not IAuthorityPluginRegistrar registrar)
{
continue;
}
if (string.IsNullOrWhiteSpace(registrar.PluginType))
{
logger?.LogWarning(
"Authority plugin registrar '{RegistrarType}' returned an empty plugin type and will be ignored.",
type.FullName);
continue;
}
if (lookup.TryGetValue(registrar.PluginType, out var existing))
{
logger?.LogWarning(
"Multiple registrars detected for plugin type '{PluginType}'. Replacing '{ExistingType}' with '{RegistrarType}'.",
registrar.PluginType,
existing.GetType().FullName,
type.FullName);
}
lookup[registrar.PluginType] = registrar;
}
catch (Exception ex)
{
logger?.LogError(
ex,
"Failed to instantiate Authority plugin registrar '{RegistrarType}'.",
type.FullName);
}
registrars.Add(type);
}
}
return lookup;
return registrars;
}
private static RegistrarActivation? TryResolveActivationForManifest(
IServiceCollection services,
string pluginType,
IReadOnlyList<Type> registrarCandidates,
IDictionary<string, Type> pluginTypeLookup,
IDictionary<Type, string> registrarTypeLookup,
ILogger? logger,
out Type? resolvedType)
{
resolvedType = null;
if (pluginTypeLookup.TryGetValue(pluginType, out var cachedType))
{
var cachedActivation = CreateRegistrarActivation(services, cachedType, logger);
if (cachedActivation is null)
{
pluginTypeLookup.Remove(pluginType);
registrarTypeLookup.Remove(cachedType);
return null;
}
resolvedType = cachedType;
return cachedActivation;
}
foreach (var candidate in registrarCandidates)
{
if (registrarTypeLookup.TryGetValue(candidate, out var knownType))
{
if (string.IsNullOrWhiteSpace(knownType))
{
continue;
}
if (string.Equals(knownType, pluginType, StringComparison.OrdinalIgnoreCase))
{
var activation = CreateRegistrarActivation(services, candidate, logger);
if (activation is null)
{
registrarTypeLookup.Remove(candidate);
pluginTypeLookup.Remove(knownType);
return null;
}
resolvedType = candidate;
return activation;
}
continue;
}
var attempt = CreateRegistrarActivation(services, candidate, logger);
if (attempt is null)
{
registrarTypeLookup[candidate] = string.Empty;
continue;
}
var candidateType = attempt.Registrar.PluginType;
if (string.IsNullOrWhiteSpace(candidateType))
{
logger?.LogWarning(
"Authority plugin registrar '{RegistrarType}' reported an empty plugin type and will be ignored.",
candidate.FullName);
registrarTypeLookup[candidate] = string.Empty;
attempt.Dispose();
continue;
}
registrarTypeLookup[candidate] = candidateType;
pluginTypeLookup[candidateType] = candidate;
if (string.Equals(candidateType, pluginType, StringComparison.OrdinalIgnoreCase))
{
resolvedType = candidate;
return attempt;
}
attempt.Dispose();
}
return null;
}
private static RegistrarActivation? CreateRegistrarActivation(IServiceCollection services, Type registrarType, ILogger? logger)
{
ServiceProvider? provider = null;
IServiceScope? scope = null;
try
{
provider = services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true
});
scope = provider.CreateScope();
var registrar = (IAuthorityPluginRegistrar)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, registrarType);
return new RegistrarActivation(provider, scope, registrar);
}
catch (Exception ex)
{
logger?.LogError(
ex,
"Failed to activate Authority plugin registrar '{RegistrarType}'.",
registrarType.FullName);
scope?.Dispose();
provider?.Dispose();
return null;
}
}
private sealed class RegistrarActivation : IDisposable
{
private readonly ServiceProvider provider;
private readonly IServiceScope scope;
public RegistrarActivation(ServiceProvider provider, IServiceScope scope, IAuthorityPluginRegistrar registrar)
{
this.provider = provider;
this.scope = scope;
Registrar = registrar;
}
public IAuthorityPluginRegistrar Registrar { get; }
public void Dispose()
{
scope.Dispose();
provider.Dispose();
}
}
private static bool IsAssemblyLoaded(

View File

@@ -416,24 +416,24 @@ if (authorityOptions.Bootstrap.Enabled)
return Results.BadRequest(new { error = "invite_provider_mismatch", message = "Invite is limited to a different identity provider." });
}
if (string.IsNullOrWhiteSpace(providerName) || !registry.TryGet(providerName!, out var provider))
if (string.IsNullOrWhiteSpace(providerName) || !registry.TryGet(providerName!, out var providerMetadata))
{
await ReleaseInviteAsync("Specified identity provider was not found.");
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, "Specified identity provider was not found.", null, request.Username, providerName, request.Roles ?? Array.Empty<string>(), inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_provider", message = "Specified identity provider was not found." });
}
if (!provider.Capabilities.SupportsPassword)
if (!providerMetadata.Capabilities.SupportsPassword)
{
await ReleaseInviteAsync("Selected provider does not support password provisioning.");
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, "Selected provider does not support password provisioning.", null, request.Username, provider.Name, request.Roles ?? Array.Empty<string>(), inviteToken).ConfigureAwait(false);
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, "Selected provider does not support password provisioning.", null, request.Username, providerMetadata.Name, request.Roles ?? Array.Empty<string>(), inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "unsupported_provider", message = "Selected provider does not support password provisioning." });
}
if (string.IsNullOrWhiteSpace(request.Username) || string.IsNullOrEmpty(request.Password))
{
await ReleaseInviteAsync("Username and password are required.");
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, "Username and password are required.", null, request.Username, provider.Name, request.Roles ?? Array.Empty<string>(), inviteToken).ConfigureAwait(false);
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, "Username and password are required.", null, request.Username, providerMetadata.Name, request.Roles ?? Array.Empty<string>(), inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_request", message = "Username and password are required." });
}
@@ -458,6 +458,9 @@ if (authorityOptions.Bootstrap.Enabled)
roles,
attributes);
await using var providerHandle = await registry.AcquireAsync(providerMetadata.Name, cancellationToken).ConfigureAwait(false);
var provider = providerHandle.Provider;
try
{
var result = await provider.Credentials.UpsertUserAsync(registration, cancellationToken).ConfigureAwait(false);
@@ -465,7 +468,7 @@ if (authorityOptions.Bootstrap.Enabled)
if (!result.Succeeded || result.Value is null)
{
await ReleaseInviteAsync(result.Message ?? "User provisioning failed.");
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, result.Message ?? "User provisioning failed.", null, request.Username, provider.Name, roles, inviteToken).ConfigureAwait(false);
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Failure, result.Message ?? "User provisioning failed.", null, request.Username, providerMetadata.Name, roles, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = result.ErrorCode ?? "bootstrap_failed", message = result.Message ?? "User provisioning failed." });
}
@@ -478,11 +481,11 @@ if (authorityOptions.Bootstrap.Enabled)
}
}
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Success, null, result.Value.SubjectId, result.Value.Username, provider.Name, roles, inviteToken).ConfigureAwait(false);
await WriteBootstrapUserAuditAsync(AuthEventOutcome.Success, null, result.Value.SubjectId, result.Value.Username, providerMetadata.Name, roles, inviteToken).ConfigureAwait(false);
return Results.Ok(new
{
provider = provider.Name,
provider = providerMetadata.Name,
subjectId = result.Value.SubjectId,
username = result.Value.Username
});
@@ -701,24 +704,34 @@ if (authorityOptions.Bootstrap.Enabled)
return Results.BadRequest(new { error = "invite_provider_mismatch", message = "Invite is limited to a different identity provider." });
}
if (string.IsNullOrWhiteSpace(providerName) || !registry.TryGet(providerName!, out var provider))
if (string.IsNullOrWhiteSpace(providerName) || !registry.TryGet(providerName!, out var providerMetadata))
{
await ReleaseInviteAsync("Specified identity provider was not found.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Specified identity provider was not found.", request.ClientId, null, providerName, request.AllowedScopes ?? Array.Empty<string>(), request?.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_provider", message = "Specified identity provider was not found." });
}
if (!provider.Capabilities.SupportsClientProvisioning || provider.ClientProvisioning is null)
if (!providerMetadata.Capabilities.SupportsClientProvisioning)
{
await ReleaseInviteAsync("Selected provider does not support client provisioning.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Selected provider does not support client provisioning.", request.ClientId, null, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Selected provider does not support client provisioning.", request.ClientId, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "unsupported_provider", message = "Selected provider does not support client provisioning." });
}
await using var providerHandle = await registry.AcquireAsync(providerMetadata.Name, cancellationToken).ConfigureAwait(false);
var provider = providerHandle.Provider;
if (provider.ClientProvisioning is null)
{
await ReleaseInviteAsync("Selected provider does not support client provisioning.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Selected provider does not support client provisioning.", request.ClientId, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "unsupported_provider", message = "Selected provider does not support client provisioning." });
}
if (string.IsNullOrWhiteSpace(request.ClientId))
{
await ReleaseInviteAsync("ClientId is required.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "ClientId is required.", null, null, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "ClientId is required.", null, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_request", message = "ClientId is required." });
}
@@ -732,7 +745,7 @@ if (authorityOptions.Bootstrap.Enabled)
if (request.Confidential && string.IsNullOrWhiteSpace(request.ClientSecret))
{
await ReleaseInviteAsync("Confidential clients require a client secret.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Confidential clients require a client secret.", request.ClientId, null, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Confidential clients require a client secret.", request.ClientId, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_request", message = "Confidential clients require a client secret." });
}
@@ -740,7 +753,7 @@ if (authorityOptions.Bootstrap.Enabled)
{
var errorMessage = redirectError ?? "Redirect URI validation failed.";
await ReleaseInviteAsync(errorMessage);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, errorMessage, request.ClientId, null, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, errorMessage, request.ClientId, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_request", message = errorMessage });
}
@@ -748,7 +761,7 @@ if (authorityOptions.Bootstrap.Enabled)
{
var errorMessage = postLogoutError ?? "Post-logout redirect URI validation failed.";
await ReleaseInviteAsync(errorMessage);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, errorMessage, request.ClientId, null, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, errorMessage, request.ClientId, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_request", message = errorMessage });
}
@@ -765,7 +778,7 @@ if (authorityOptions.Bootstrap.Enabled)
if (binding is null || string.IsNullOrWhiteSpace(binding.Thumbprint))
{
await ReleaseInviteAsync("Certificate binding thumbprint is required.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Certificate binding thumbprint is required.", request.ClientId, null, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, "Certificate binding thumbprint is required.", request.ClientId, null, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = "invalid_request", message = "Certificate binding thumbprint is required." });
}
@@ -801,7 +814,7 @@ if (authorityOptions.Bootstrap.Enabled)
if (!result.Succeeded || result.Value is null)
{
await ReleaseInviteAsync(result.Message ?? "Client provisioning failed.");
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, result.Message ?? "Client provisioning failed.", request.ClientId, result.Value?.ClientId, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Failure, result.Message ?? "Client provisioning failed.", request.ClientId, result.Value?.ClientId, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.BadRequest(new { error = result.ErrorCode ?? "bootstrap_failed", message = result.Message ?? "Client provisioning failed." });
}
@@ -814,11 +827,11 @@ if (authorityOptions.Bootstrap.Enabled)
}
}
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Success, null, request.ClientId, result.Value.ClientId, provider.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
await WriteBootstrapClientAuditAsync(AuthEventOutcome.Success, null, request.ClientId, result.Value.ClientId, providerMetadata.Name, request.AllowedScopes ?? Array.Empty<string>(), request.Confidential, inviteToken).ConfigureAwait(false);
return Results.Ok(new
{
provider = provider.Name,
provider = providerMetadata.Name,
clientId = result.Value.ClientId,
confidential = result.Value.Confidential
});
@@ -1169,12 +1182,13 @@ app.UseAuthorization();
app.MapGet("/health", async (IAuthorityIdentityProviderRegistry registry, CancellationToken cancellationToken) =>
{
var pluginHealth = new List<object>();
foreach (var provider in registry.Providers)
foreach (var providerMetadata in registry.Providers)
{
var health = await provider.CheckHealthAsync(cancellationToken).ConfigureAwait(false);
await using var handle = await registry.AcquireAsync(providerMetadata.Name, cancellationToken).ConfigureAwait(false);
var health = await handle.Provider.CheckHealthAsync(cancellationToken).ConfigureAwait(false);
pluginHealth.Add(new
{
provider = provider.Name,
provider = providerMetadata.Name,
status = health.Status.ToString().ToLowerInvariant(),
message = health.Message
});

View File

@@ -20,13 +20,13 @@
| AUTHCORE-STORAGE-DEVICE-TOKENS | DONE (2025-10-14) | Authority Core, Storage Guild | AUTHCORE-BUILD-OPENIDDICT | Reintroduce `AuthorityTokenDeviceDocument` + projections removed during refactor so storage layer compiles. | ✅ Document type restored with mappings/migrations; ✅ Storage tests cover device artifacts; ✅ Authority solution build green. |
| AUTHCORE-BOOTSTRAP-INVITES | DONE (2025-10-14) | Authority Core, DevOps | AUTHCORE-STORAGE-DEVICE-TOKENS | Wire bootstrap invite cleanup service against restored document schema and re-enable lifecycle tests. | ✅ `BootstrapInviteCleanupService` passes integration tests; ✅ Operator guide updated if behavior changes; ✅ Build/test matrices green. |
| AUTHSTORAGE-MONGO-08-001 | DONE (2025-10-19) | Authority Core & Storage Guild | — | Harden Mongo session usage with causal consistency for mutations and follow-up reads. | • Scoped middleware/service creates `IClientSessionHandle` with causal consistency + majority read/write concerns<br>• Stores accept optional session parameter and reuse it for write + immediate reads<br>• GraphQL/HTTP pipelines updated to flow session through post-mutation queries<br>• Replica-set integration test exercises primary election and verifies read-your-write guarantees |
| AUTH-PLUGIN-COORD-08-002 | DOING (2025-10-19) | Authority Core, Plugin Platform Guild | PLUGIN-DI-08-001 | Coordinate scoped-service adoption for Authority plug-in registrars and background jobs ahead of PLUGIN-DI-08-002 implementation. | ✅ Workshop locked for 2025-10-20 15:0016:00UTC; ✅ Pre-read checklist in `docs/dev/authority-plugin-di-coordination.md`; ✅ Follow-up tasks captured in module backlogs before code changes begin. |
| AUTH-DPOP-11-001 | DOING (2025-10-19) | Authority Core & Security Guild | — | Implement DPoP proof validation + nonce handling for high-value audiences per architecture. | • Proof handler validates method/uri/hash + replay; nonce issuing/consumption implemented for in-memory + Redis stores<br>• Client credential path stamps `cnf.jkt` and persists sender metadata<br>• Remaining: finalize Redis configuration surface (docs/sample config), unskip nonce-challenge regression once HTTP pipeline emits high-value audiences, refresh operator docs |
> Remark (2025-10-19): DPoP handler now seeds request resources/audiences from client metadata; nonce challenge integration test re-enabled (still requires full suite once Concelier build restored).
| AUTH-PLUGIN-COORD-08-002 | DONE (2025-10-20) | Authority Core, Plugin Platform Guild | PLUGIN-DI-08-001 | Coordinate scoped-service adoption for Authority plug-in registrars and background jobs ahead of PLUGIN-DI-08-002 implementation. | ✅ Workshop completed 2025-10-20 15:0016:05UTC with notes/action log in `docs/dev/authority-plugin-di-coordination.md`; ✅ Follow-up backlog updates assigned via documented action items ahead of PLUGIN-DI-08-002 delivery. |
| AUTH-DPOP-11-001 | DONE (2025-10-20) | Authority Core & Security Guild | — | Implement DPoP proof validation + nonce handling for high-value audiences per architecture. | ✅ Redis-configurable nonce store surfaced via `security.senderConstraints.dpop.nonce` with sample YAML and architecture docs refreshed<br>✅ High-value audience enforcement uses normalised required audiences to avoid whitespace/case drift<br>✅ Operator guide updated with Redis-backed nonce snippet and env-var override guidance; integration test already covers nonce challenge |
> Remark (2025-10-20): `etc/authority.yaml.sample` gains senderConstraint sections (rate limits, DPoP, mTLS), docs (`docs/ARCHITECTURE_AUTHORITY.md`, `docs/11_AUTHORITY.md`, plan) refreshed. `ResolveNonceAudience` now relies on `NormalizedAudiences` and options trim persisted values. `dotnet test StellaOps.Authority.sln` attempted (2025-10-20 15:12UTC) but failed on `NU1900` because the mirrored NuGet service index `https://mirrors.ablera.dev/nuget/nuget-mirror/v3/index.json` was unreachable; no project build executed.
| AUTH-MTLS-11-002 | DOING (2025-10-19) | Authority Core & Security Guild | — | Add OAuth mTLS client credential support with certificate-bound tokens and introspection updates. | • Certificate validator scaffold plus cnf stamping present; tokens persist sender thumbprints<br>• Remaining: provisioning/storage for certificate bindings, SAN/CA validation, introspection propagation, integration tests/docs before marking DONE |
> Remark (2025-10-19): Client provisioning accepts certificate bindings; validator enforces SAN types/CA allow-list with rotation grace; mtls integration tests updated (full suite still blocked by upstream build).
> Remark (2025-10-19, AUTHSTORAGE-MONGO-08-001): Prerequisites re-checked (none outstanding). Session accessor wired through Authority pipeline; stores accept optional sessions; added replica-set election regression test for read-your-write.
> Remark (2025-10-19, AUTH-DPOP-11-001): Handler, nonce store, and persistence hooks merged; Redis-backed configuration + end-to-end nonce enforcement still open. Full solution test blocked by `StellaOps.Concelier.Storage.Mongo` compile errors.
> Remark (2025-10-19, AUTH-DPOP-11-001): Handler, nonce store, and persistence hooks merged; Redis-backed configuration + end-to-end nonce enforcement still open. (Superseded by 2025-10-20 update above.)
> Remark (2025-10-19, AUTH-MTLS-11-002): Certificate validator + cnf stamping delivered; binding storage, CA/SAN validation, integration suites outstanding before status can move to DONE.
> Update status columns (TODO / DOING / DONE / BLOCKED) together with code changes. Always run `dotnet test src/StellaOps.Authority.sln` when touching host logic.

View File

@@ -951,6 +951,15 @@ public sealed class CommandHandlersTests
public Task<RuntimePolicyEvaluationResult> EvaluateRuntimePolicyAsync(RuntimePolicyEvaluationRequest request, CancellationToken cancellationToken)
=> Task.FromResult(RuntimePolicyResult);
public Task<OfflineKitDownloadResult> DownloadOfflineKitAsync(string? bundleId, string destinationDirectory, bool overwrite, bool resume, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public Task<OfflineKitImportResult> ImportOfflineKitAsync(OfflineKitImportRequest request, CancellationToken cancellationToken)
=> throw new NotSupportedException();
public Task<OfflineKitStatus> GetOfflineKitStatusAsync(CancellationToken cancellationToken)
=> throw new NotSupportedException();
}
private sealed class StubExecutor : IScannerExecutor

View File

@@ -1,24 +1,25 @@
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using StellaOps.Auth.Abstractions;
using StellaOps.Auth.Client;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Services;
using StellaOps.Cli.Services.Models;
using StellaOps.Cli.Services.Models.Transport;
using StellaOps.Cli.Tests.Testing;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using StellaOps.Auth.Abstractions;
using StellaOps.Auth.Client;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Services;
using StellaOps.Cli.Services.Models;
using StellaOps.Cli.Services.Models.Transport;
using StellaOps.Cli.Tests.Testing;
using System.Linq;
namespace StellaOps.Cli.Tests.Services;
@@ -481,7 +482,352 @@ public sealed class BackendOperationsClientTests
Assert.Equal("manual-override", Assert.IsType<string>(secondary.AdditionalProperties["quietedBy"]));
}
private sealed class StubTokenClient : IStellaOpsTokenClient
[Fact]
public async Task DownloadOfflineKitAsync_DownloadsBundleAndWritesMetadata()
{
using var temp = new TempDirectory();
var bundleBytes = Encoding.UTF8.GetBytes("bundle-data");
var manifestBytes = Encoding.UTF8.GetBytes("{\"artifacts\":[]}");
var bundleDigest = Convert.ToHexString(SHA256.HashData(bundleBytes)).ToLowerInvariant();
var manifestDigest = Convert.ToHexString(SHA256.HashData(manifestBytes)).ToLowerInvariant();
var metadataPayload = JsonSerializer.Serialize(new
{
bundleId = "2025-10-20-full",
bundleName = "stella-ops-offline-kit-2025-10-20.tgz",
bundleSha256 = $"sha256:{bundleDigest}",
bundleSize = (long)bundleBytes.Length,
bundleUrl = "https://mirror.example/stella-ops-offline-kit-2025-10-20.tgz",
bundleSignatureName = "stella-ops-offline-kit-2025-10-20.tgz.sig",
bundleSignatureUrl = "https://mirror.example/stella-ops-offline-kit-2025-10-20.tgz.sig",
manifestName = "offline-manifest-2025-10-20.json",
manifestSha256 = $"sha256:{manifestDigest}",
manifestUrl = "https://mirror.example/offline-manifest-2025-10-20.json",
manifestSignatureName = "offline-manifest-2025-10-20.json.jws",
manifestSignatureUrl = "https://mirror.example/offline-manifest-2025-10-20.json.jws",
capturedAt = DateTimeOffset.UtcNow
}, new JsonSerializerOptions(JsonSerializerDefaults.Web));
var handler = new StubHttpMessageHandler(
(request, _) =>
{
Assert.Equal("https://backend.example/api/offline-kit/bundles/latest", request.RequestUri!.ToString());
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(metadataPayload)
};
},
(request, _) =>
{
var absolute = request.RequestUri!.AbsoluteUri;
if (absolute.EndsWith(".tgz", StringComparison.OrdinalIgnoreCase))
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(bundleBytes)
};
}
if (absolute.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(manifestBytes)
};
}
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(Array.Empty<byte>())
};
});
var httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://backend.example")
};
var options = new StellaOpsCliOptions
{
BackendUrl = "https://backend.example",
Offline = new StellaOpsCliOfflineOptions
{
KitsDirectory = temp.Path
}
};
var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug));
var client = new BackendOperationsClient(httpClient, options, loggerFactory.CreateLogger<BackendOperationsClient>());
var result = await client.DownloadOfflineKitAsync(null, temp.Path, overwrite: false, resume: false, CancellationToken.None);
Assert.False(result.FromCache);
Assert.True(File.Exists(result.BundlePath));
Assert.True(File.Exists(result.ManifestPath));
Assert.NotNull(result.BundleSignaturePath);
Assert.NotNull(result.ManifestSignaturePath);
Assert.True(File.Exists(result.MetadataPath));
using var metadata = JsonDocument.Parse(File.ReadAllText(result.MetadataPath));
Assert.Equal("2025-10-20-full", metadata.RootElement.GetProperty("bundleId").GetString());
Assert.Equal(bundleDigest, metadata.RootElement.GetProperty("bundleSha256").GetString());
}
[Fact]
public async Task DownloadOfflineKitAsync_ResumesPartialDownload()
{
using var temp = new TempDirectory();
var bundleBytes = Encoding.UTF8.GetBytes("partial-download-data");
var manifestBytes = Encoding.UTF8.GetBytes("{\"manifest\":true}");
var bundleDigest = Convert.ToHexString(SHA256.HashData(bundleBytes)).ToLowerInvariant();
var manifestDigest = Convert.ToHexString(SHA256.HashData(manifestBytes)).ToLowerInvariant();
var metadataJson = JsonSerializer.Serialize(new
{
bundleId = "2025-10-21-full",
bundleName = "kit.tgz",
bundleSha256 = bundleDigest,
bundleSize = (long)bundleBytes.Length,
bundleUrl = "https://mirror.example/kit.tgz",
manifestName = "offline-manifest.json",
manifestSha256 = manifestDigest,
manifestUrl = "https://mirror.example/offline-manifest.json",
capturedAt = DateTimeOffset.UtcNow
}, new JsonSerializerOptions(JsonSerializerDefaults.Web));
var partialPath = Path.Combine(temp.Path, "kit.tgz.partial");
await File.WriteAllBytesAsync(partialPath, bundleBytes.AsSpan(0, bundleBytes.Length / 2).ToArray());
var handler = new StubHttpMessageHandler(
(request, _) => new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(metadataJson)
},
(request, _) =>
{
if (request.RequestUri!.AbsoluteUri.EndsWith("kit.tgz", StringComparison.OrdinalIgnoreCase))
{
Assert.NotNull(request.Headers.Range);
Assert.Equal(bundleBytes.Length / 2, request.Headers.Range!.Ranges.Single().From);
return new HttpResponseMessage(HttpStatusCode.PartialContent)
{
Content = new ByteArrayContent(bundleBytes.AsSpan(bundleBytes.Length / 2).ToArray())
};
}
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(manifestBytes)
};
});
var httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://backend.example")
};
var options = new StellaOpsCliOptions
{
BackendUrl = "https://backend.example",
Offline = new StellaOpsCliOfflineOptions
{
KitsDirectory = temp.Path
}
};
var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug));
var client = new BackendOperationsClient(httpClient, options, loggerFactory.CreateLogger<BackendOperationsClient>());
var result = await client.DownloadOfflineKitAsync(null, temp.Path, overwrite: false, resume: true, CancellationToken.None);
Assert.Equal(bundleDigest, result.Descriptor.BundleSha256);
Assert.Equal(bundleBytes.Length, new FileInfo(result.BundlePath).Length);
}
[Fact]
public async Task ImportOfflineKitAsync_SendsMultipartPayload()
{
using var temp = new TempDirectory();
var bundlePath = Path.Combine(temp.Path, "kit.tgz");
var manifestPath = Path.Combine(temp.Path, "offline-manifest.json");
var bundleBytes = Encoding.UTF8.GetBytes("bundle-content");
var manifestBytes = Encoding.UTF8.GetBytes("{\"manifest\":true}");
await File.WriteAllBytesAsync(bundlePath, bundleBytes);
await File.WriteAllBytesAsync(manifestPath, manifestBytes);
var bundleDigest = Convert.ToHexString(SHA256.HashData(bundleBytes)).ToLowerInvariant();
var manifestDigest = Convert.ToHexString(SHA256.HashData(manifestBytes)).ToLowerInvariant();
var metadata = new OfflineKitMetadataDocument
{
BundleId = "2025-10-21-full",
BundleName = "kit.tgz",
BundleSha256 = bundleDigest,
BundleSize = bundleBytes.Length,
BundlePath = bundlePath,
CapturedAt = DateTimeOffset.UtcNow,
DownloadedAt = DateTimeOffset.UtcNow,
Channel = "stable",
Kind = "full",
ManifestName = "offline-manifest.json",
ManifestSha256 = manifestDigest,
ManifestSize = manifestBytes.Length,
ManifestPath = manifestPath,
IsDelta = false,
BaseBundleId = null
};
await File.WriteAllTextAsync(bundlePath + ".metadata.json", JsonSerializer.Serialize(metadata, new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true }));
var recordingHandler = new ImportRecordingHandler();
var httpClient = new HttpClient(recordingHandler)
{
BaseAddress = new Uri("https://backend.example")
};
var options = new StellaOpsCliOptions
{
BackendUrl = "https://backend.example"
};
var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug));
var client = new BackendOperationsClient(httpClient, options, loggerFactory.CreateLogger<BackendOperationsClient>());
var request = new OfflineKitImportRequest(
bundlePath,
manifestPath,
null,
null,
metadata.BundleId,
metadata.BundleSha256,
metadata.BundleSize,
metadata.CapturedAt,
metadata.Channel,
metadata.Kind,
metadata.IsDelta,
metadata.BaseBundleId,
metadata.ManifestSha256,
metadata.ManifestSize);
var result = await client.ImportOfflineKitAsync(request, CancellationToken.None);
Assert.Equal("imp-1", result.ImportId);
Assert.NotNull(recordingHandler.MetadataJson);
Assert.NotNull(recordingHandler.BundlePayload);
Assert.NotNull(recordingHandler.ManifestPayload);
using var metadataJson = JsonDocument.Parse(recordingHandler.MetadataJson!);
Assert.Equal(bundleDigest, metadataJson.RootElement.GetProperty("bundleSha256").GetString());
Assert.Equal(manifestDigest, metadataJson.RootElement.GetProperty("manifestSha256").GetString());
}
[Fact]
public async Task GetOfflineKitStatusAsync_ParsesResponse()
{
var captured = DateTimeOffset.UtcNow;
var imported = captured.AddMinutes(5);
var statusJson = JsonSerializer.Serialize(new
{
current = new
{
bundleId = "2025-10-22-full",
channel = "stable",
kind = "full",
isDelta = false,
baseBundleId = (string?)null,
bundleSha256 = "sha256:abc123",
bundleSize = 42,
capturedAt = captured,
importedAt = imported
},
components = new[]
{
new
{
name = "concelier-json",
version = "2025-10-22",
digest = "sha256:def456",
capturedAt = captured,
sizeBytes = 1234
}
}
}, new JsonSerializerOptions(JsonSerializerDefaults.Web));
var handler = new StubHttpMessageHandler(
(request, _) =>
{
Assert.Equal("https://backend.example/api/offline-kit/status", request.RequestUri!.ToString());
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(statusJson)
};
});
var httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://backend.example")
};
var options = new StellaOpsCliOptions
{
BackendUrl = "https://backend.example"
};
var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug));
var client = new BackendOperationsClient(httpClient, options, loggerFactory.CreateLogger<BackendOperationsClient>());
var status = await client.GetOfflineKitStatusAsync(CancellationToken.None);
Assert.Equal("2025-10-22-full", status.BundleId);
Assert.Equal("stable", status.Channel);
Assert.Equal("full", status.Kind);
Assert.False(status.IsDelta);
Assert.Equal(42, status.BundleSize);
Assert.Single(status.Components);
Assert.Equal("concelier-json", status.Components[0].Name);
}
private sealed class ImportRecordingHandler : HttpMessageHandler
{
public string? MetadataJson { get; private set; }
public byte[]? BundlePayload { get; private set; }
public byte[]? ManifestPayload { get; private set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri!.AbsoluteUri.EndsWith("/api/offline-kit/import", StringComparison.OrdinalIgnoreCase))
{
Assert.IsType<MultipartFormDataContent>(request.Content);
foreach (var part in (MultipartFormDataContent)request.Content!)
{
var name = part.Headers.ContentDisposition?.Name?.Trim('"');
switch (name)
{
case "metadata":
MetadataJson = await part.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
break;
case "bundle":
BundlePayload = await part.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false);
break;
case "manifest":
ManifestPayload = await part.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false);
break;
}
}
}
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("{\"importId\":\"imp-1\",\"status\":\"queued\",\"submittedAt\":\"2025-10-21T00:00:00Z\"}")
};
}
}
private sealed class StubTokenClient : IStellaOpsTokenClient
{
private readonly StellaOpsTokenResult _tokenResult;

View File

@@ -27,6 +27,7 @@ internal static class CommandFactory
root.Add(BuildExcititorCommand(services, verboseOption, cancellationToken));
root.Add(BuildRuntimeCommand(services, verboseOption, cancellationToken));
root.Add(BuildAuthCommand(services, options, verboseOption, cancellationToken));
root.Add(BuildOfflineCommand(services, verboseOption, cancellationToken));
root.Add(BuildConfigCommand(options));
return root;
@@ -606,11 +607,102 @@ internal static class CommandFactory
return auth;
}
private static Command BuildConfigCommand(StellaOpsCliOptions options)
{
var config = new Command("config", "Inspect CLI configuration state.");
var show = new Command("show", "Display resolved configuration values.");
private static Command BuildOfflineCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var offline = new Command("offline", "Offline kit workflows and utilities.");
var kit = new Command("kit", "Manage offline kit bundles.");
var pull = new Command("pull", "Download the latest offline kit bundle.");
var bundleIdOption = new Option<string?>("--bundle-id")
{
Description = "Optional bundle identifier. Defaults to the latest available."
};
var destinationOption = new Option<string?>("--destination")
{
Description = "Directory to store downloaded bundles (defaults to the configured offline kits directory)."
};
var overwriteOption = new Option<bool>("--overwrite")
{
Description = "Overwrite existing files even if checksums match."
};
var noResumeOption = new Option<bool>("--no-resume")
{
Description = "Disable resuming partial downloads."
};
pull.Add(bundleIdOption);
pull.Add(destinationOption);
pull.Add(overwriteOption);
pull.Add(noResumeOption);
pull.SetAction((parseResult, _) =>
{
var bundleId = parseResult.GetValue(bundleIdOption);
var destination = parseResult.GetValue(destinationOption);
var overwrite = parseResult.GetValue(overwriteOption);
var resume = !parseResult.GetValue(noResumeOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleOfflineKitPullAsync(services, bundleId, destination, overwrite, resume, verbose, cancellationToken);
});
var import = new Command("import", "Upload an offline kit bundle to the backend.");
var bundleArgument = new Argument<string>("bundle")
{
Description = "Path to the offline kit tarball (.tgz)."
};
var manifestOption = new Option<string?>("--manifest")
{
Description = "Offline manifest JSON path (defaults to metadata or sibling file)."
};
var bundleSignatureOption = new Option<string?>("--bundle-signature")
{
Description = "Detached signature for the offline bundle (e.g. .sig)."
};
var manifestSignatureOption = new Option<string?>("--manifest-signature")
{
Description = "Detached signature for the offline manifest (e.g. .jws)."
};
import.Add(bundleArgument);
import.Add(manifestOption);
import.Add(bundleSignatureOption);
import.Add(manifestSignatureOption);
import.SetAction((parseResult, _) =>
{
var bundlePath = parseResult.GetValue(bundleArgument) ?? string.Empty;
var manifest = parseResult.GetValue(manifestOption);
var bundleSignature = parseResult.GetValue(bundleSignatureOption);
var manifestSignature = parseResult.GetValue(manifestSignatureOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleOfflineKitImportAsync(services, bundlePath, manifest, bundleSignature, manifestSignature, verbose, cancellationToken);
});
var status = new Command("status", "Display offline kit installation status.");
var jsonOption = new Option<bool>("--json")
{
Description = "Emit status as JSON."
};
status.Add(jsonOption);
status.SetAction((parseResult, _) =>
{
var asJson = parseResult.GetValue(jsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleOfflineKitStatusAsync(services, asJson, verbose, cancellationToken);
});
kit.Add(pull);
kit.Add(import);
kit.Add(status);
offline.Add(kit);
return offline;
}
private static Command BuildConfigCommand(StellaOpsCliOptions options)
{
var config = new Command("config", "Inspect CLI configuration state.");
var show = new Command("show", "Display resolved configuration values.");
show.SetAction((_, _) =>
{
var authority = options.Authority ?? new StellaOpsCliAuthorityOptions();

View File

@@ -1448,17 +1448,415 @@ internal static class CommandHandlers
{
logger.LogError(ex, "Failed to verify revocation bundle.");
Environment.ExitCode = 1;
}
finally
{
loggerFactory.Dispose();
}
}
private static bool TryParseDetachedJws(string value, out string encodedHeader, out string encodedSignature)
{
encodedHeader = string.Empty;
encodedSignature = string.Empty;
}
finally
{
loggerFactory.Dispose();
}
}
public static async Task HandleOfflineKitPullAsync(
IServiceProvider services,
string? bundleId,
string? destinationDirectory,
bool overwrite,
bool resume,
bool verbose,
CancellationToken cancellationToken)
{
await using var scope = services.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IBackendOperationsClient>();
var options = scope.ServiceProvider.GetRequiredService<StellaOpsCliOptions>();
var logger = scope.ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("offline-kit-pull");
var verbosity = scope.ServiceProvider.GetRequiredService<VerbosityState>();
var previousLevel = verbosity.MinimumLevel;
verbosity.MinimumLevel = verbose ? LogLevel.Debug : LogLevel.Information;
using var activity = CliActivitySource.Instance.StartActivity("cli.offline.kit.pull", ActivityKind.Client);
activity?.SetTag("stellaops.cli.bundle_id", string.IsNullOrWhiteSpace(bundleId) ? "latest" : bundleId);
using var duration = CliMetrics.MeasureCommandDuration("offline kit pull");
try
{
var targetDirectory = string.IsNullOrWhiteSpace(destinationDirectory)
? options.Offline?.KitsDirectory ?? Path.Combine(Environment.CurrentDirectory, "offline-kits")
: destinationDirectory;
targetDirectory = Path.GetFullPath(targetDirectory);
Directory.CreateDirectory(targetDirectory);
var result = await client.DownloadOfflineKitAsync(bundleId, targetDirectory, overwrite, resume, cancellationToken).ConfigureAwait(false);
logger.LogInformation(
"Bundle {BundleId} stored at {Path} (captured {Captured:u}, sha256:{Digest}).",
result.Descriptor.BundleId,
result.BundlePath,
result.Descriptor.CapturedAt,
result.Descriptor.BundleSha256);
logger.LogInformation("Manifest saved to {Manifest}.", result.ManifestPath);
if (!string.IsNullOrWhiteSpace(result.MetadataPath))
{
logger.LogDebug("Metadata recorded at {Metadata}.", result.MetadataPath);
}
if (result.BundleSignaturePath is not null)
{
logger.LogInformation("Bundle signature saved to {Signature}.", result.BundleSignaturePath);
}
if (result.ManifestSignaturePath is not null)
{
logger.LogInformation("Manifest signature saved to {Signature}.", result.ManifestSignaturePath);
}
CliMetrics.RecordOfflineKitDownload(result.Descriptor.Kind ?? "unknown", result.FromCache);
activity?.SetTag("stellaops.cli.bundle_cache", result.FromCache);
Environment.ExitCode = 0;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to download offline kit bundle.");
Environment.ExitCode = 1;
}
finally
{
verbosity.MinimumLevel = previousLevel;
}
}
public static async Task HandleOfflineKitImportAsync(
IServiceProvider services,
string bundlePath,
string? manifestPath,
string? bundleSignaturePath,
string? manifestSignaturePath,
bool verbose,
CancellationToken cancellationToken)
{
await using var scope = services.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IBackendOperationsClient>();
var options = scope.ServiceProvider.GetRequiredService<StellaOpsCliOptions>();
var logger = scope.ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("offline-kit-import");
var verbosity = scope.ServiceProvider.GetRequiredService<VerbosityState>();
var previousLevel = verbosity.MinimumLevel;
verbosity.MinimumLevel = verbose ? LogLevel.Debug : LogLevel.Information;
using var activity = CliActivitySource.Instance.StartActivity("cli.offline.kit.import", ActivityKind.Client);
using var duration = CliMetrics.MeasureCommandDuration("offline kit import");
try
{
if (string.IsNullOrWhiteSpace(bundlePath))
{
logger.LogError("Bundle path is required.");
Environment.ExitCode = 1;
return;
}
bundlePath = Path.GetFullPath(bundlePath);
if (!File.Exists(bundlePath))
{
logger.LogError("Bundle file {Path} not found.", bundlePath);
Environment.ExitCode = 1;
return;
}
var metadata = await LoadOfflineKitMetadataAsync(bundlePath, cancellationToken).ConfigureAwait(false);
if (metadata is not null)
{
manifestPath ??= metadata.ManifestPath;
bundleSignaturePath ??= metadata.BundleSignaturePath;
manifestSignaturePath ??= metadata.ManifestSignaturePath;
}
manifestPath = NormalizeFilePath(manifestPath);
bundleSignaturePath = NormalizeFilePath(bundleSignaturePath);
manifestSignaturePath = NormalizeFilePath(manifestSignaturePath);
if (manifestPath is null)
{
manifestPath = TryInferManifestPath(bundlePath);
if (manifestPath is not null)
{
logger.LogDebug("Using inferred manifest path {Path}.", manifestPath);
}
}
if (manifestPath is not null && !File.Exists(manifestPath))
{
logger.LogError("Manifest file {Path} not found.", manifestPath);
Environment.ExitCode = 1;
return;
}
if (bundleSignaturePath is not null && !File.Exists(bundleSignaturePath))
{
logger.LogWarning("Bundle signature {Path} not found; skipping.", bundleSignaturePath);
bundleSignaturePath = null;
}
if (manifestSignaturePath is not null && !File.Exists(manifestSignaturePath))
{
logger.LogWarning("Manifest signature {Path} not found; skipping.", manifestSignaturePath);
manifestSignaturePath = null;
}
if (metadata is not null)
{
var computedBundleDigest = await ComputeSha256Async(bundlePath, cancellationToken).ConfigureAwait(false);
if (!DigestsEqual(computedBundleDigest, metadata.BundleSha256))
{
logger.LogError("Bundle digest mismatch. Expected sha256:{Expected} but computed sha256:{Actual}.", metadata.BundleSha256, computedBundleDigest);
Environment.ExitCode = 1;
return;
}
if (manifestPath is not null)
{
var computedManifestDigest = await ComputeSha256Async(manifestPath, cancellationToken).ConfigureAwait(false);
if (!DigestsEqual(computedManifestDigest, metadata.ManifestSha256))
{
logger.LogError("Manifest digest mismatch. Expected sha256:{Expected} but computed sha256:{Actual}.", metadata.ManifestSha256, computedManifestDigest);
Environment.ExitCode = 1;
return;
}
}
}
var request = new OfflineKitImportRequest(
bundlePath,
manifestPath,
bundleSignaturePath,
manifestSignaturePath,
metadata?.BundleId,
metadata?.BundleSha256,
metadata?.BundleSize,
metadata?.CapturedAt,
metadata?.Channel,
metadata?.Kind,
metadata?.IsDelta,
metadata?.BaseBundleId,
metadata?.ManifestSha256,
metadata?.ManifestSize);
var result = await client.ImportOfflineKitAsync(request, cancellationToken).ConfigureAwait(false);
CliMetrics.RecordOfflineKitImport(result.Status);
logger.LogInformation(
"Import {ImportId} submitted at {Submitted:u} with status {Status}.",
string.IsNullOrWhiteSpace(result.ImportId) ? "<pending>" : result.ImportId,
result.SubmittedAt,
string.IsNullOrWhiteSpace(result.Status) ? "queued" : result.Status);
if (!string.IsNullOrWhiteSpace(result.Message))
{
logger.LogInformation(result.Message);
}
Environment.ExitCode = 0;
}
catch (Exception ex)
{
logger.LogError(ex, "Offline kit import failed.");
Environment.ExitCode = 1;
}
finally
{
verbosity.MinimumLevel = previousLevel;
}
}
public static async Task HandleOfflineKitStatusAsync(
IServiceProvider services,
bool asJson,
bool verbose,
CancellationToken cancellationToken)
{
await using var scope = services.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IBackendOperationsClient>();
var logger = scope.ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("offline-kit-status");
var verbosity = scope.ServiceProvider.GetRequiredService<VerbosityState>();
var previousLevel = verbosity.MinimumLevel;
verbosity.MinimumLevel = verbose ? LogLevel.Debug : LogLevel.Information;
using var activity = CliActivitySource.Instance.StartActivity("cli.offline.kit.status", ActivityKind.Client);
using var duration = CliMetrics.MeasureCommandDuration("offline kit status");
try
{
var status = await client.GetOfflineKitStatusAsync(cancellationToken).ConfigureAwait(false);
if (asJson)
{
var payload = new
{
bundleId = status.BundleId,
channel = status.Channel,
kind = status.Kind,
isDelta = status.IsDelta,
baseBundleId = status.BaseBundleId,
capturedAt = status.CapturedAt,
importedAt = status.ImportedAt,
sha256 = status.BundleSha256,
sizeBytes = status.BundleSize,
components = status.Components.Select(component => new
{
component.Name,
component.Version,
component.Digest,
component.CapturedAt,
component.SizeBytes
})
};
var json = JsonSerializer.Serialize(payload, new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true });
Console.WriteLine(json);
}
else
{
if (string.IsNullOrWhiteSpace(status.BundleId))
{
logger.LogInformation("No offline kit bundle has been imported yet.");
}
else
{
logger.LogInformation(
"Current bundle {BundleId} ({Kind}) captured {Captured:u}, imported {Imported:u}, sha256:{Digest}, size {Size}.",
status.BundleId,
status.Kind ?? "unknown",
status.CapturedAt ?? default,
status.ImportedAt ?? default,
status.BundleSha256 ?? "<n/a>",
status.BundleSize.HasValue ? status.BundleSize.Value.ToString("N0", CultureInfo.InvariantCulture) : "<n/a>");
}
if (status.Components.Count > 0)
{
var table = new Table().AddColumns("Component", "Version", "Digest", "Captured", "Size (bytes)");
foreach (var component in status.Components)
{
table.AddRow(
component.Name,
string.IsNullOrWhiteSpace(component.Version) ? "-" : component.Version!,
string.IsNullOrWhiteSpace(component.Digest) ? "-" : $"sha256:{component.Digest}",
component.CapturedAt?.ToString("u", CultureInfo.InvariantCulture) ?? "-",
component.SizeBytes.HasValue ? component.SizeBytes.Value.ToString("N0", CultureInfo.InvariantCulture) : "-");
}
AnsiConsole.Write(table);
}
}
Environment.ExitCode = 0;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to read offline kit status.");
Environment.ExitCode = 1;
}
finally
{
verbosity.MinimumLevel = previousLevel;
}
}
private static async Task<OfflineKitMetadataDocument?> LoadOfflineKitMetadataAsync(string bundlePath, CancellationToken cancellationToken)
{
var metadataPath = bundlePath + ".metadata.json";
if (!File.Exists(metadataPath))
{
return null;
}
try
{
await using var stream = File.OpenRead(metadataPath);
return await JsonSerializer.DeserializeAsync<OfflineKitMetadataDocument>(stream, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch
{
return null;
}
}
private static string? NormalizeFilePath(string? path)
{
if (string.IsNullOrWhiteSpace(path))
{
return null;
}
return Path.GetFullPath(path);
}
private static string? TryInferManifestPath(string bundlePath)
{
var directory = Path.GetDirectoryName(bundlePath);
if (string.IsNullOrWhiteSpace(directory))
{
return null;
}
var baseName = Path.GetFileName(bundlePath);
if (string.IsNullOrWhiteSpace(baseName))
{
return null;
}
baseName = Path.GetFileNameWithoutExtension(baseName);
if (baseName.EndsWith(".tar", StringComparison.OrdinalIgnoreCase))
{
baseName = Path.GetFileNameWithoutExtension(baseName);
}
var candidates = new[]
{
Path.Combine(directory, $"offline-manifest-{baseName}.json"),
Path.Combine(directory, "offline-manifest.json")
};
foreach (var candidate in candidates)
{
if (File.Exists(candidate))
{
return Path.GetFullPath(candidate);
}
}
return Directory.EnumerateFiles(directory, "offline-manifest*.json").FirstOrDefault();
}
private static bool DigestsEqual(string computed, string? expected)
{
if (string.IsNullOrWhiteSpace(expected))
{
return true;
}
return string.Equals(NormalizeDigest(computed), NormalizeDigest(expected), StringComparison.OrdinalIgnoreCase);
}
private static string NormalizeDigest(string digest)
{
var value = digest.Trim();
if (value.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase))
{
value = value.Substring("sha256:".Length);
}
return value.ToLowerInvariant();
}
private static async Task<string> ComputeSha256Async(string path, CancellationToken cancellationToken)
{
await using var stream = File.OpenRead(path);
var hash = await SHA256.HashDataAsync(stream, cancellationToken).ConfigureAwait(false);
return Convert.ToHexString(hash).ToLowerInvariant();
}
private static bool TryParseDetachedJws(string value, out string encodedHeader, out string encodedSignature)
{
encodedHeader = string.Empty;
encodedSignature = string.Empty;
if (string.IsNullOrWhiteSpace(value))
{

View File

@@ -200,6 +200,40 @@ public static class CliBootstrapper
{
authority.TokenCacheDirectory = Path.GetFullPath(authority.TokenCacheDirectory);
}
cliOptions.Offline ??= new StellaOpsCliOfflineOptions();
var offline = cliOptions.Offline;
var kitsDirectory = ResolveWithFallback(
string.Empty,
configuration,
"STELLAOPS_OFFLINE_KITS_DIRECTORY",
"STELLAOPS_OFFLINE_KITS_DIR",
"StellaOps:Offline:KitsDirectory",
"StellaOps:Offline:KitDirectory",
"Offline:KitsDirectory",
"Offline:KitDirectory");
if (string.IsNullOrWhiteSpace(kitsDirectory))
{
kitsDirectory = offline.KitsDirectory ?? "offline-kits";
}
offline.KitsDirectory = Path.GetFullPath(kitsDirectory);
if (!Directory.Exists(offline.KitsDirectory))
{
Directory.CreateDirectory(offline.KitsDirectory);
}
var mirror = ResolveWithFallback(
string.Empty,
configuration,
"STELLAOPS_OFFLINE_MIRROR_URL",
"StellaOps:Offline:KitMirror",
"Offline:KitMirror",
"Offline:MirrorUrl");
offline.MirrorUrl = string.IsNullOrWhiteSpace(mirror) ? null : mirror.Trim();
};
});

View File

@@ -9,7 +9,7 @@ public sealed class StellaOpsCliOptions
public string ApiKey { get; set; } = string.Empty;
public string BackendUrl { get; set; } = string.Empty;
public string ScannerCacheDirectory { get; set; } = "scanners";
public string ResultsDirectory { get; set; } = "results";
@@ -23,6 +23,8 @@ public sealed class StellaOpsCliOptions
public int ScanUploadAttempts { get; set; } = 3;
public StellaOpsCliAuthorityOptions Authority { get; set; } = new();
public StellaOpsCliOfflineOptions Offline { get; set; } = new();
}
public sealed class StellaOpsCliAuthorityOptions
@@ -54,3 +56,10 @@ public sealed class StellaOpsCliAuthorityResilienceOptions
public TimeSpan? OfflineCacheTolerance { get; set; }
}
public sealed class StellaOpsCliOfflineOptions
{
public string KitsDirectory { get; set; } = "offline-kits";
public string? MirrorUrl { get; set; }
}

View File

@@ -535,7 +535,687 @@ internal sealed class BackendOperationsClient : IBackendOperationsClient
return list;
}
private static List<string> NormalizeImages(IReadOnlyList<string> images)
public async Task<OfflineKitDownloadResult> DownloadOfflineKitAsync(string? bundleId, string destinationDirectory, bool overwrite, bool resume, CancellationToken cancellationToken)
{
EnsureBackendConfigured();
var rootDirectory = ResolveOfflineDirectory(destinationDirectory);
Directory.CreateDirectory(rootDirectory);
var descriptor = await FetchOfflineKitDescriptorAsync(bundleId, cancellationToken).ConfigureAwait(false);
var bundlePath = Path.Combine(rootDirectory, descriptor.BundleName);
var metadataPath = bundlePath + ".metadata.json";
var manifestPath = Path.Combine(rootDirectory, descriptor.ManifestName);
var bundleSignaturePath = descriptor.BundleSignatureName is not null ? Path.Combine(rootDirectory, descriptor.BundleSignatureName) : null;
var manifestSignaturePath = descriptor.ManifestSignatureName is not null ? Path.Combine(rootDirectory, descriptor.ManifestSignatureName) : null;
var fromCache = false;
if (!overwrite && File.Exists(bundlePath))
{
var digest = await ComputeSha256Async(bundlePath, cancellationToken).ConfigureAwait(false);
if (string.Equals(digest, descriptor.BundleSha256, StringComparison.OrdinalIgnoreCase))
{
fromCache = true;
}
else if (resume)
{
var partial = bundlePath + ".partial";
File.Move(bundlePath, partial, overwrite: true);
}
else
{
File.Delete(bundlePath);
}
}
if (!fromCache)
{
await DownloadFileWithResumeAsync(descriptor.BundleDownloadUri, bundlePath, descriptor.BundleSha256, descriptor.BundleSize, resume, cancellationToken).ConfigureAwait(false);
}
await DownloadFileWithResumeAsync(descriptor.ManifestDownloadUri, manifestPath, descriptor.ManifestSha256, descriptor.ManifestSize ?? 0, resume: false, cancellationToken).ConfigureAwait(false);
if (descriptor.BundleSignatureDownloadUri is not null && bundleSignaturePath is not null)
{
await DownloadAuxiliaryFileAsync(descriptor.BundleSignatureDownloadUri, bundleSignaturePath, cancellationToken).ConfigureAwait(false);
}
if (descriptor.ManifestSignatureDownloadUri is not null && manifestSignaturePath is not null)
{
await DownloadAuxiliaryFileAsync(descriptor.ManifestSignatureDownloadUri, manifestSignaturePath, cancellationToken).ConfigureAwait(false);
}
await WriteOfflineKitMetadataAsync(metadataPath, descriptor, bundlePath, manifestPath, bundleSignaturePath, manifestSignaturePath, cancellationToken).ConfigureAwait(false);
return new OfflineKitDownloadResult(
descriptor,
bundlePath,
manifestPath,
bundleSignaturePath,
manifestSignaturePath,
metadataPath,
fromCache);
}
public async Task<OfflineKitImportResult> ImportOfflineKitAsync(OfflineKitImportRequest request, CancellationToken cancellationToken)
{
EnsureBackendConfigured();
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}
var bundlePath = Path.GetFullPath(request.BundlePath);
if (!File.Exists(bundlePath))
{
throw new FileNotFoundException("Offline kit bundle not found.", bundlePath);
}
string? manifestPath = null;
if (!string.IsNullOrWhiteSpace(request.ManifestPath))
{
manifestPath = Path.GetFullPath(request.ManifestPath);
if (!File.Exists(manifestPath))
{
throw new FileNotFoundException("Offline kit manifest not found.", manifestPath);
}
}
string? bundleSignaturePath = null;
if (!string.IsNullOrWhiteSpace(request.BundleSignaturePath))
{
bundleSignaturePath = Path.GetFullPath(request.BundleSignaturePath);
if (!File.Exists(bundleSignaturePath))
{
throw new FileNotFoundException("Offline kit bundle signature not found.", bundleSignaturePath);
}
}
string? manifestSignaturePath = null;
if (!string.IsNullOrWhiteSpace(request.ManifestSignaturePath))
{
manifestSignaturePath = Path.GetFullPath(request.ManifestSignaturePath);
if (!File.Exists(manifestSignaturePath))
{
throw new FileNotFoundException("Offline kit manifest signature not found.", manifestSignaturePath);
}
}
var bundleSize = request.BundleSize ?? new FileInfo(bundlePath).Length;
var bundleSha = string.IsNullOrWhiteSpace(request.BundleSha256)
? await ComputeSha256Async(bundlePath, cancellationToken).ConfigureAwait(false)
: NormalizeSha(request.BundleSha256) ?? throw new InvalidOperationException("Bundle digest must not be empty.");
string? manifestSha = null;
long? manifestSize = null;
if (manifestPath is not null)
{
manifestSize = request.ManifestSize ?? new FileInfo(manifestPath).Length;
manifestSha = string.IsNullOrWhiteSpace(request.ManifestSha256)
? await ComputeSha256Async(manifestPath, cancellationToken).ConfigureAwait(false)
: NormalizeSha(request.ManifestSha256);
}
var metadata = new OfflineKitImportMetadataPayload
{
BundleId = request.BundleId,
BundleSha256 = bundleSha,
BundleSize = bundleSize,
CapturedAt = request.CapturedAt,
Channel = request.Channel,
Kind = request.Kind,
IsDelta = request.IsDelta,
BaseBundleId = request.BaseBundleId,
ManifestSha256 = manifestSha,
ManifestSize = manifestSize
};
using var message = CreateRequest(HttpMethod.Post, "api/offline-kit/import");
await AuthorizeRequestAsync(message, cancellationToken).ConfigureAwait(false);
using var content = new MultipartFormDataContent();
var metadataOptions = new JsonSerializerOptions(SerializerOptions)
{
WriteIndented = false
};
var metadataJson = JsonSerializer.Serialize(metadata, metadataOptions);
var metadataContent = new StringContent(metadataJson, Encoding.UTF8, "application/json");
content.Add(metadataContent, "metadata");
var bundleStream = File.OpenRead(bundlePath);
var bundleContent = new StreamContent(bundleStream);
bundleContent.Headers.ContentType = new MediaTypeHeaderValue("application/gzip");
content.Add(bundleContent, "bundle", Path.GetFileName(bundlePath));
if (manifestPath is not null)
{
var manifestStream = File.OpenRead(manifestPath);
var manifestContent = new StreamContent(manifestStream);
manifestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Add(manifestContent, "manifest", Path.GetFileName(manifestPath));
}
if (bundleSignaturePath is not null)
{
var signatureStream = File.OpenRead(bundleSignaturePath);
var signatureContent = new StreamContent(signatureStream);
signatureContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(signatureContent, "bundleSignature", Path.GetFileName(bundleSignaturePath));
}
if (manifestSignaturePath is not null)
{
var manifestSignatureStream = File.OpenRead(manifestSignaturePath);
var manifestSignatureContent = new StreamContent(manifestSignatureStream);
manifestSignatureContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(manifestSignatureContent, "manifestSignature", Path.GetFileName(manifestSignaturePath));
}
message.Content = content;
using var response = await _httpClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var failure = await CreateFailureMessageAsync(response, cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException(failure);
}
OfflineKitImportResponseTransport? document;
try
{
document = await response.Content.ReadFromJsonAsync<OfflineKitImportResponseTransport>(SerializerOptions, cancellationToken).ConfigureAwait(false);
}
catch (JsonException ex)
{
var raw = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException($"Failed to parse offline kit import response. {ex.Message}", ex)
{
Data = { ["payload"] = raw }
};
}
var submittedAt = document?.SubmittedAt ?? DateTimeOffset.UtcNow;
return new OfflineKitImportResult(
document?.ImportId,
document?.Status,
submittedAt,
document?.Message);
}
public async Task<OfflineKitStatus> GetOfflineKitStatusAsync(CancellationToken cancellationToken)
{
EnsureBackendConfigured();
using var request = CreateRequest(HttpMethod.Get, "api/offline-kit/status");
await AuthorizeRequestAsync(request, cancellationToken).ConfigureAwait(false);
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var failure = await CreateFailureMessageAsync(response, cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException(failure);
}
if (response.Content is null || response.Content.Headers.ContentLength is 0)
{
return new OfflineKitStatus(null, null, null, false, null, null, null, null, null, Array.Empty<OfflineKitComponentStatus>());
}
OfflineKitStatusTransport? document;
try
{
document = await response.Content.ReadFromJsonAsync<OfflineKitStatusTransport>(SerializerOptions, cancellationToken).ConfigureAwait(false);
}
catch (JsonException ex)
{
var raw = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException($"Failed to parse offline kit status response. {ex.Message}", ex)
{
Data = { ["payload"] = raw }
};
}
var current = document?.Current;
var components = MapOfflineComponents(document?.Components);
if (current is null)
{
return new OfflineKitStatus(null, null, null, false, null, null, null, null, null, components);
}
return new OfflineKitStatus(
NormalizeOptionalString(current.BundleId),
NormalizeOptionalString(current.Channel),
NormalizeOptionalString(current.Kind),
current.IsDelta ?? false,
NormalizeOptionalString(current.BaseBundleId),
current.CapturedAt?.ToUniversalTime(),
current.ImportedAt?.ToUniversalTime(),
NormalizeSha(current.BundleSha256),
current.BundleSize,
components);
}
private string ResolveOfflineDirectory(string destinationDirectory)
{
if (!string.IsNullOrWhiteSpace(destinationDirectory))
{
return Path.GetFullPath(destinationDirectory);
}
var configured = _options.Offline?.KitsDirectory;
if (!string.IsNullOrWhiteSpace(configured))
{
return Path.GetFullPath(configured);
}
return Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "offline-kits"));
}
private async Task<OfflineKitBundleDescriptor> FetchOfflineKitDescriptorAsync(string? bundleId, CancellationToken cancellationToken)
{
var route = string.IsNullOrWhiteSpace(bundleId)
? "api/offline-kit/bundles/latest"
: $"api/offline-kit/bundles/{Uri.EscapeDataString(bundleId)}";
using var request = CreateRequest(HttpMethod.Get, route);
await AuthorizeRequestAsync(request, cancellationToken).ConfigureAwait(false);
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var failure = await CreateFailureMessageAsync(response, cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException(failure);
}
OfflineKitBundleDescriptorTransport? payload;
try
{
payload = await response.Content.ReadFromJsonAsync<OfflineKitBundleDescriptorTransport>(SerializerOptions, cancellationToken).ConfigureAwait(false);
}
catch (JsonException ex)
{
var raw = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException($"Failed to parse offline kit metadata. {ex.Message}", ex)
{
Data = { ["payload"] = raw }
};
}
if (payload is null)
{
throw new InvalidOperationException("Offline kit metadata response was empty.");
}
return MapOfflineKitDescriptor(payload);
}
private OfflineKitBundleDescriptor MapOfflineKitDescriptor(OfflineKitBundleDescriptorTransport transport)
{
if (transport is null)
{
throw new ArgumentNullException(nameof(transport));
}
var bundleName = string.IsNullOrWhiteSpace(transport.BundleName)
? throw new InvalidOperationException("Offline kit metadata missing bundleName.")
: transport.BundleName!.Trim();
var bundleId = string.IsNullOrWhiteSpace(transport.BundleId) ? bundleName : transport.BundleId!.Trim();
var bundleSha = NormalizeSha(transport.BundleSha256) ?? throw new InvalidOperationException("Offline kit metadata missing bundleSha256.");
var bundleSize = transport.BundleSize;
if (bundleSize <= 0)
{
throw new InvalidOperationException("Offline kit metadata missing bundle size.");
}
var manifestName = string.IsNullOrWhiteSpace(transport.ManifestName) ? "offline-manifest.json" : transport.ManifestName!.Trim();
var manifestSha = NormalizeSha(transport.ManifestSha256) ?? throw new InvalidOperationException("Offline kit metadata missing manifestSha256.");
var capturedAt = transport.CapturedAt?.ToUniversalTime() ?? DateTimeOffset.UtcNow;
var bundleDownloadUri = ResolveDownloadUri(transport.BundleUrl, transport.BundlePath, bundleName);
var manifestDownloadUri = ResolveDownloadUri(transport.ManifestUrl, transport.ManifestPath, manifestName);
var bundleSignatureUri = ResolveOptionalDownloadUri(transport.BundleSignatureUrl, transport.BundleSignaturePath, transport.BundleSignatureName);
var manifestSignatureUri = ResolveOptionalDownloadUri(transport.ManifestSignatureUrl, transport.ManifestSignaturePath, transport.ManifestSignatureName);
var bundleSignatureName = ResolveArtifactName(transport.BundleSignatureName, bundleSignatureUri);
var manifestSignatureName = ResolveArtifactName(transport.ManifestSignatureName, manifestSignatureUri);
return new OfflineKitBundleDescriptor(
bundleId,
bundleName,
bundleSha,
bundleSize,
bundleDownloadUri,
manifestName,
manifestSha,
manifestDownloadUri,
capturedAt,
NormalizeOptionalString(transport.Channel),
NormalizeOptionalString(transport.Kind),
transport.IsDelta ?? false,
NormalizeOptionalString(transport.BaseBundleId),
bundleSignatureName,
bundleSignatureUri,
manifestSignatureName,
manifestSignatureUri,
transport.ManifestSize);
}
private static string? ResolveArtifactName(string? explicitName, Uri? uri)
{
if (!string.IsNullOrWhiteSpace(explicitName))
{
return explicitName.Trim();
}
if (uri is not null)
{
var name = Path.GetFileName(uri.LocalPath);
return string.IsNullOrWhiteSpace(name) ? null : name;
}
return null;
}
private Uri ResolveDownloadUri(string? absoluteOrRelativeUrl, string? relativePath, string fallbackFileName)
{
if (!string.IsNullOrWhiteSpace(absoluteOrRelativeUrl))
{
var candidate = new Uri(absoluteOrRelativeUrl, UriKind.RelativeOrAbsolute);
if (candidate.IsAbsoluteUri)
{
return candidate;
}
if (_httpClient.BaseAddress is not null)
{
return new Uri(_httpClient.BaseAddress, candidate);
}
return BuildUriFromRelative(candidate.ToString());
}
if (!string.IsNullOrWhiteSpace(relativePath))
{
return BuildUriFromRelative(relativePath);
}
if (!string.IsNullOrWhiteSpace(fallbackFileName))
{
return BuildUriFromRelative(fallbackFileName);
}
throw new InvalidOperationException("Offline kit metadata did not include a download URL.");
}
private Uri BuildUriFromRelative(string relative)
{
var normalized = relative.TrimStart('/');
if (!string.IsNullOrWhiteSpace(_options.Offline?.MirrorUrl) &&
Uri.TryCreate(_options.Offline.MirrorUrl, UriKind.Absolute, out var mirrorBase))
{
if (!mirrorBase.AbsoluteUri.EndsWith("/"))
{
mirrorBase = new Uri(mirrorBase.AbsoluteUri + "/");
}
return new Uri(mirrorBase, normalized);
}
if (_httpClient.BaseAddress is not null)
{
return new Uri(_httpClient.BaseAddress, normalized);
}
throw new InvalidOperationException($"Cannot resolve offline kit URI for '{relative}' because no mirror or backend base address is configured.");
}
private Uri? ResolveOptionalDownloadUri(string? absoluteOrRelativeUrl, string? relativePath, string? fallbackName)
{
var hasData = !string.IsNullOrWhiteSpace(absoluteOrRelativeUrl) ||
!string.IsNullOrWhiteSpace(relativePath) ||
!string.IsNullOrWhiteSpace(fallbackName);
if (!hasData)
{
return null;
}
try
{
return ResolveDownloadUri(absoluteOrRelativeUrl, relativePath, fallbackName ?? string.Empty);
}
catch
{
return null;
}
}
private async Task DownloadFileWithResumeAsync(Uri downloadUri, string targetPath, string expectedSha256, long expectedSize, bool resume, CancellationToken cancellationToken)
{
var directory = Path.GetDirectoryName(targetPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
var partialPath = resume ? targetPath + ".partial" : targetPath + ".tmp";
if (!resume && File.Exists(targetPath))
{
File.Delete(targetPath);
}
if (resume && File.Exists(targetPath))
{
File.Move(targetPath, partialPath, overwrite: true);
}
long existingLength = 0;
if (resume && File.Exists(partialPath))
{
existingLength = new FileInfo(partialPath).Length;
if (expectedSize > 0 && existingLength >= expectedSize)
{
existingLength = expectedSize;
}
}
while (true)
{
using var request = new HttpRequestMessage(HttpMethod.Get, downloadUri);
if (resume && existingLength > 0 && expectedSize > 0 && existingLength < expectedSize)
{
request.Headers.Range = new RangeHeaderValue(existingLength, null);
}
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (resume && existingLength > 0 && expectedSize > 0 && existingLength < expectedSize && response.StatusCode == HttpStatusCode.OK)
{
existingLength = 0;
if (File.Exists(partialPath))
{
File.Delete(partialPath);
}
continue;
}
if (!response.IsSuccessStatusCode &&
!(resume && existingLength > 0 && response.StatusCode == HttpStatusCode.PartialContent))
{
var failure = await CreateFailureMessageAsync(response, cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException(failure);
}
var destination = resume ? partialPath : targetPath;
var mode = resume && existingLength > 0 ? FileMode.Append : FileMode.Create;
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
await using (var file = new FileStream(destination, mode, FileAccess.Write, FileShare.None, 81920, useAsync: true))
{
await stream.CopyToAsync(file, cancellationToken).ConfigureAwait(false);
}
break;
}
if (resume && File.Exists(partialPath))
{
File.Move(partialPath, targetPath, overwrite: true);
}
var digest = await ComputeSha256Async(targetPath, cancellationToken).ConfigureAwait(false);
if (!string.Equals(digest, expectedSha256, StringComparison.OrdinalIgnoreCase))
{
File.Delete(targetPath);
throw new InvalidOperationException($"Digest mismatch for {Path.GetFileName(targetPath)}. Expected {expectedSha256} but computed {digest}.");
}
if (expectedSize > 0)
{
var actualSize = new FileInfo(targetPath).Length;
if (actualSize != expectedSize)
{
File.Delete(targetPath);
throw new InvalidOperationException($"Size mismatch for {Path.GetFileName(targetPath)}. Expected {expectedSize:N0} bytes but downloaded {actualSize:N0} bytes.");
}
}
}
private async Task DownloadAuxiliaryFileAsync(Uri downloadUri, string targetPath, CancellationToken cancellationToken)
{
var directory = Path.GetDirectoryName(targetPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
using var request = new HttpRequestMessage(HttpMethod.Get, downloadUri);
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var failure = await CreateFailureMessageAsync(response, cancellationToken).ConfigureAwait(false);
throw new InvalidOperationException(failure);
}
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
await using var file = new FileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.None, 81920, useAsync: true);
await stream.CopyToAsync(file, cancellationToken).ConfigureAwait(false);
}
private static async Task WriteOfflineKitMetadataAsync(
string metadataPath,
OfflineKitBundleDescriptor descriptor,
string bundlePath,
string manifestPath,
string? bundleSignaturePath,
string? manifestSignaturePath,
CancellationToken cancellationToken)
{
var document = new OfflineKitMetadataDocument
{
BundleId = descriptor.BundleId,
BundleName = descriptor.BundleName,
BundleSha256 = descriptor.BundleSha256,
BundleSize = descriptor.BundleSize,
BundlePath = Path.GetFullPath(bundlePath),
CapturedAt = descriptor.CapturedAt,
DownloadedAt = DateTimeOffset.UtcNow,
Channel = descriptor.Channel,
Kind = descriptor.Kind,
IsDelta = descriptor.IsDelta,
BaseBundleId = descriptor.BaseBundleId,
ManifestName = descriptor.ManifestName,
ManifestSha256 = descriptor.ManifestSha256,
ManifestSize = descriptor.ManifestSize,
ManifestPath = Path.GetFullPath(manifestPath),
BundleSignaturePath = bundleSignaturePath is null ? null : Path.GetFullPath(bundleSignaturePath),
ManifestSignaturePath = manifestSignaturePath is null ? null : Path.GetFullPath(manifestSignaturePath)
};
var options = new JsonSerializerOptions(SerializerOptions)
{
WriteIndented = true
};
var payload = JsonSerializer.Serialize(document, options);
await File.WriteAllTextAsync(metadataPath, payload, cancellationToken).ConfigureAwait(false);
}
private static IReadOnlyList<OfflineKitComponentStatus> MapOfflineComponents(List<OfflineKitComponentStatusTransport>? transports)
{
if (transports is null || transports.Count == 0)
{
return Array.Empty<OfflineKitComponentStatus>();
}
var list = new List<OfflineKitComponentStatus>();
foreach (var transport in transports)
{
if (transport is null || string.IsNullOrWhiteSpace(transport.Name))
{
continue;
}
list.Add(new OfflineKitComponentStatus(
transport.Name.Trim(),
NormalizeOptionalString(transport.Version),
NormalizeSha(transport.Digest),
transport.CapturedAt?.ToUniversalTime(),
transport.SizeBytes));
}
return list.Count == 0 ? Array.Empty<OfflineKitComponentStatus>() : list;
}
private static string? NormalizeSha(string? digest)
{
if (string.IsNullOrWhiteSpace(digest))
{
return null;
}
var value = digest.Trim();
if (value.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase))
{
value = value.Substring("sha256:".Length);
}
return value.ToLowerInvariant();
}
private sealed class OfflineKitImportMetadataPayload
{
public string? BundleId { get; set; }
public string BundleSha256 { get; set; } = string.Empty;
public long BundleSize { get; set; }
public DateTimeOffset? CapturedAt { get; set; }
public string? Channel { get; set; }
public string? Kind { get; set; }
public bool? IsDelta { get; set; }
public string? BaseBundleId { get; set; }
public string? ManifestSha256 { get; set; }
public long? ManifestSize { get; set; }
}
private static List<string> NormalizeImages(IReadOnlyList<string> images)
{
var normalized = new List<string>();
if (images is null)

View File

@@ -22,4 +22,10 @@ internal interface IBackendOperationsClient
Task<IReadOnlyList<ExcititorProviderSummary>> GetExcititorProvidersAsync(bool includeDisabled, CancellationToken cancellationToken);
Task<RuntimePolicyEvaluationResult> EvaluateRuntimePolicyAsync(RuntimePolicyEvaluationRequest request, CancellationToken cancellationToken);
Task<OfflineKitDownloadResult> DownloadOfflineKitAsync(string? bundleId, string destinationDirectory, bool overwrite, bool resume, CancellationToken cancellationToken);
Task<OfflineKitImportResult> ImportOfflineKitAsync(OfflineKitImportRequest request, CancellationToken cancellationToken);
Task<OfflineKitStatus> GetOfflineKitStatusAsync(CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
namespace StellaOps.Cli.Services.Models;
internal sealed record OfflineKitBundleDescriptor(
string BundleId,
string BundleName,
string BundleSha256,
long BundleSize,
Uri BundleDownloadUri,
string ManifestName,
string ManifestSha256,
Uri ManifestDownloadUri,
DateTimeOffset CapturedAt,
string? Channel,
string? Kind,
bool IsDelta,
string? BaseBundleId,
string? BundleSignatureName,
Uri? BundleSignatureDownloadUri,
string? ManifestSignatureName,
Uri? ManifestSignatureDownloadUri,
long? ManifestSize);
internal sealed record OfflineKitDownloadResult(
OfflineKitBundleDescriptor Descriptor,
string BundlePath,
string ManifestPath,
string? BundleSignaturePath,
string? ManifestSignaturePath,
string MetadataPath,
bool FromCache);
internal sealed record OfflineKitImportRequest(
string BundlePath,
string? ManifestPath,
string? BundleSignaturePath,
string? ManifestSignaturePath,
string? BundleId,
string? BundleSha256,
long? BundleSize,
DateTimeOffset? CapturedAt,
string? Channel,
string? Kind,
bool? IsDelta,
string? BaseBundleId,
string? ManifestSha256,
long? ManifestSize);
internal sealed record OfflineKitImportResult(
string? ImportId,
string? Status,
DateTimeOffset SubmittedAt,
string? Message);
internal sealed record OfflineKitStatus(
string? BundleId,
string? Channel,
string? Kind,
bool IsDelta,
string? BaseBundleId,
DateTimeOffset? CapturedAt,
DateTimeOffset? ImportedAt,
string? BundleSha256,
long? BundleSize,
IReadOnlyList<OfflineKitComponentStatus> Components);
internal sealed record OfflineKitComponentStatus(
string Name,
string? Version,
string? Digest,
DateTimeOffset? CapturedAt,
long? SizeBytes);
internal sealed record OfflineKitMetadataDocument
{
public string? BundleId { get; init; }
public string BundleName { get; init; } = string.Empty;
public string BundleSha256 { get; init; } = string.Empty;
public long BundleSize { get; init; }
public string BundlePath { get; init; } = string.Empty;
public DateTimeOffset CapturedAt { get; init; }
public DateTimeOffset DownloadedAt { get; init; }
public string? Channel { get; init; }
public string? Kind { get; init; }
public bool IsDelta { get; init; }
public string? BaseBundleId { get; init; }
public string ManifestName { get; init; } = string.Empty;
public string ManifestSha256 { get; init; } = string.Empty;
public long? ManifestSize { get; init; }
public string ManifestPath { get; init; } = string.Empty;
public string? BundleSignaturePath { get; init; }
public string? ManifestSignaturePath { get; init; }
}

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
namespace StellaOps.Cli.Services.Models.Transport;
internal sealed class OfflineKitBundleDescriptorTransport
{
public string? BundleId { get; set; }
public string? BundleName { get; set; }
public string? BundleSha256 { get; set; }
public long BundleSize { get; set; }
public string? BundleUrl { get; set; }
public string? BundlePath { get; set; }
public string? BundleSignatureName { get; set; }
public string? BundleSignatureUrl { get; set; }
public string? BundleSignaturePath { get; set; }
public string? ManifestName { get; set; }
public string? ManifestSha256 { get; set; }
public long? ManifestSize { get; set; }
public string? ManifestUrl { get; set; }
public string? ManifestPath { get; set; }
public string? ManifestSignatureName { get; set; }
public string? ManifestSignatureUrl { get; set; }
public string? ManifestSignaturePath { get; set; }
public DateTimeOffset? CapturedAt { get; set; }
public string? Channel { get; set; }
public string? Kind { get; set; }
public bool? IsDelta { get; set; }
public string? BaseBundleId { get; set; }
}
internal sealed class OfflineKitStatusBundleTransport
{
public string? BundleId { get; set; }
public string? Channel { get; set; }
public string? Kind { get; set; }
public bool? IsDelta { get; set; }
public string? BaseBundleId { get; set; }
public string? BundleSha256 { get; set; }
public long? BundleSize { get; set; }
public DateTimeOffset? CapturedAt { get; set; }
public DateTimeOffset? ImportedAt { get; set; }
}
internal sealed class OfflineKitStatusTransport
{
public OfflineKitStatusBundleTransport? Current { get; set; }
public List<OfflineKitComponentStatusTransport>? Components { get; set; }
}
internal sealed class OfflineKitComponentStatusTransport
{
public string? Name { get; set; }
public string? Version { get; set; }
public string? Digest { get; set; }
public DateTimeOffset? CapturedAt { get; set; }
public long? SizeBytes { get; set; }
}
internal sealed class OfflineKitImportResponseTransport
{
public string? ImportId { get; set; }
public string? Status { get; set; }
public DateTimeOffset? SubmittedAt { get; set; }
public string? Message { get; set; }
}

View File

@@ -18,7 +18,7 @@ If you are working on this file you need to read docs/ARCHITECTURE_EXCITITOR.md
|EXCITITOR-CLI-01-002 Export download & attestation UX|DevEx/CLI|EXCITITOR-CLI-01-001, EXCITITOR-EXPORT-01-001|DONE (2025-10-19) CLI export prints digest/size/Rekor metadata, `--output` downloads with SHA-256 verification + cache reuse, and unit coverage validated via `dotnet test src/StellaOps.Cli.Tests`.|
|EXCITITOR-CLI-01-003 CLI docs & examples for Excititor|Docs/CLI|EXCITITOR-CLI-01-001|**DOING (2025-10-19)** Update docs/09_API_CLI_REFERENCE.md and quickstart snippets to cover Excititor verbs, offline guidance, and attestation verification workflow.|
|CLI-RUNTIME-13-005 Runtime policy test verbs|DevEx/CLI|SCANNER-RUNTIME-12-302, ZASTAVA-WEBHOOK-12-102|**DONE (2025-10-19)** Added `runtime policy test` command (stdin/file support, JSON output), backend client method + typed models, verdict table output, docs/tests updated (`dotnet test src/StellaOps.Cli.Tests`).|
|CLI-OFFLINE-13-006 Offline kit workflows|DevEx/CLI|DEVOPS-OFFLINE-14-002|TODO Implement `offline kit pull/import/status` commands with integrity checks, resumable downloads, and doc updates.|
|CLI-OFFLINE-13-006 Offline kit workflows|DevEx/CLI|DEVOPS-OFFLINE-14-002|**DONE (2025-10-21)** Added `offline kit pull/import/status` commands with resumable downloads, digest/metadata validation, metrics, docs updates, and regression coverage (`dotnet test src/StellaOps.Cli.Tests`).|
|CLI-PLUGIN-13-007 Plugin packaging|DevEx/CLI|CLI-RUNTIME-13-005, CLI-OFFLINE-13-006|TODO Package non-core verbs as restart-time plug-ins (manifest + loader updates, tests ensuring no hot reload).|
|CLI-RUNTIME-13-008 Runtime policy contract sync|DevEx/CLI, Scanner WebService Guild|SCANNER-RUNTIME-12-302|**DONE (2025-10-19)** CLI runtime table/JSON now align with SCANNER-RUNTIME-12-302 (SBOM referrers, quieted provenance, confidence, verified Rekor); docs/09 updated with joint sign-off note.|
|CLI-RUNTIME-13-009 Runtime policy smoke fixture|DevEx/CLI, QA Guild|CLI-RUNTIME-13-005|**DONE (2025-10-19)** Spectre console harness + regression tests cover table and `--json` output paths for `runtime policy test`, using stubbed backend and integrated into `dotnet test` suite.|

View File

@@ -7,14 +7,16 @@ internal static class CliMetrics
{
private static readonly Meter Meter = new("StellaOps.Cli", "1.0.0");
private static readonly Counter<long> ScannerDownloadCounter = Meter.CreateCounter<long>("stellaops.cli.scanner.download.count");
private static readonly Counter<long> ScannerInstallCounter = Meter.CreateCounter<long>("stellaops.cli.scanner.install.count");
private static readonly Counter<long> ScanRunCounter = Meter.CreateCounter<long>("stellaops.cli.scan.run.count");
private static readonly Histogram<double> CommandDurationHistogram = Meter.CreateHistogram<double>("stellaops.cli.command.duration.ms");
public static void RecordScannerDownload(string channel, bool fromCache)
=> ScannerDownloadCounter.Add(1, new KeyValuePair<string, object?>[]
{
private static readonly Counter<long> ScannerDownloadCounter = Meter.CreateCounter<long>("stellaops.cli.scanner.download.count");
private static readonly Counter<long> ScannerInstallCounter = Meter.CreateCounter<long>("stellaops.cli.scanner.install.count");
private static readonly Counter<long> ScanRunCounter = Meter.CreateCounter<long>("stellaops.cli.scan.run.count");
private static readonly Counter<long> OfflineKitDownloadCounter = Meter.CreateCounter<long>("stellaops.cli.offline.kit.download.count");
private static readonly Counter<long> OfflineKitImportCounter = Meter.CreateCounter<long>("stellaops.cli.offline.kit.import.count");
private static readonly Histogram<double> CommandDurationHistogram = Meter.CreateHistogram<double>("stellaops.cli.command.duration.ms");
public static void RecordScannerDownload(string channel, bool fromCache)
=> ScannerDownloadCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("channel", channel),
new("cache", fromCache ? "hit" : "miss")
});
@@ -23,16 +25,29 @@ internal static class CliMetrics
=> ScannerInstallCounter.Add(1, new KeyValuePair<string, object?>[] { new("channel", channel) });
public static void RecordScanRun(string runner, int exitCode)
=> ScanRunCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("runner", runner),
new("exit_code", exitCode)
});
public static IDisposable MeasureCommandDuration(string command)
{
var start = DateTime.UtcNow;
return new DurationScope(command, start);
=> ScanRunCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("runner", runner),
new("exit_code", exitCode)
});
public static void RecordOfflineKitDownload(string kind, bool fromCache)
=> OfflineKitDownloadCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("kind", string.IsNullOrWhiteSpace(kind) ? "unknown" : kind),
new("cache", fromCache ? "hit" : "miss")
});
public static void RecordOfflineKitImport(string? status)
=> OfflineKitImportCounter.Add(1, new KeyValuePair<string, object?>[]
{
new("status", string.IsNullOrWhiteSpace(status) ? "queued" : status)
});
public static IDisposable MeasureCommandDuration(string command)
{
var start = DateTime.UtcNow;
return new DurationScope(command, start);
}
private sealed class DurationScope : IDisposable

View File

@@ -0,0 +1,33 @@
using System;
using System.IO;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
internal static class FixtureLoader
{
private static readonly string FixturesRoot = Path.Combine(AppContext.BaseDirectory, "Fixtures");
public static string Read(string relativePath)
{
if (string.IsNullOrWhiteSpace(relativePath))
{
throw new ArgumentException("Fixture path must be provided.", nameof(relativePath));
}
var normalized = relativePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
var path = Path.Combine(FixturesRoot, normalized);
if (!File.Exists(path))
{
throw new FileNotFoundException($"Fixture '{relativePath}' not found at '{path}'.", path);
}
var content = File.ReadAllText(path);
return NormalizeLineEndings(content);
}
public static string Normalize(string value) => NormalizeLineEndings(value);
private static string NormalizeLineEndings(string value)
=> value.Replace("\r\n", "\n", StringComparison.Ordinal);
}

View File

@@ -0,0 +1,212 @@
{
"advisoryKey": "CVE-2025-1111",
"affectedPackages": [
{
"type": "semver",
"identifier": "pkg:npm/example@1.0.0",
"platform": null,
"versionRanges": [
{
"fixedVersion": "1.2.0",
"introducedVersion": "1.0.0",
"lastAffectedVersion": null,
"primitives": {
"evr": null,
"hasVendorExtensions": false,
"nevra": null,
"semVer": {
"constraintExpression": ">=1.0.0,<1.2.0",
"exactValue": null,
"fixed": "1.2.0",
"fixedInclusive": false,
"introduced": "1.0.0",
"introducedInclusive": true,
"lastAffected": null,
"lastAffectedInclusive": true,
"style": "range"
},
"vendorExtensions": null
},
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "range",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[].versionranges[]"
]
},
"rangeExpression": ">=1.0.0,<1.2.0",
"rangeKind": "semver"
}
],
"normalizedVersions": [
{
"scheme": "semver",
"type": "range",
"min": "1.0.0",
"minInclusive": true,
"max": "1.2.0",
"maxInclusive": false,
"value": null,
"notes": null
}
],
"statuses": [
{
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "status",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[].statuses[]"
]
},
"status": "fixed"
}
],
"provenance": [
{
"source": "ghsa",
"kind": "map",
"value": "package",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[]"
]
},
{
"source": "stellaops-mirror",
"kind": "map",
"value": "domain=primary;repository=mirror-primary;generated=2025-10-19T12:00:00.0000000+00:00;package=pkg:npm/example@1.0.0",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[]",
"affectedpackages[].normalizedversions[]",
"affectedpackages[].statuses[]",
"affectedpackages[].versionranges[]"
]
}
]
}
],
"aliases": [
"CVE-2025-1111",
"GHSA-xxxx-xxxx-xxxx"
],
"canonicalMetricId": "cvss::ghsa::CVE-2025-1111",
"credits": [
{
"displayName": "Security Researcher",
"role": "reporter",
"contacts": [
"mailto:researcher@example.com"
],
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "credit",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"credits[]"
]
}
}
],
"cvssMetrics": [
{
"baseScore": 9.8,
"baseSeverity": "critical",
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "cvss",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"cvssmetrics[]"
]
},
"vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"version": "3.1"
}
],
"cwes": [
{
"taxonomy": "cwe",
"identifier": "CWE-79",
"name": "Cross-site Scripting",
"uri": "https://cwe.mitre.org/data/definitions/79.html",
"provenance": [
{
"source": "ghsa",
"kind": "map",
"value": "cwe",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"cwes[]"
]
}
]
}
],
"description": "Deterministic test payload distributed via mirror.",
"exploitKnown": false,
"language": "en",
"modified": "2025-10-11T00:00:00+00:00",
"provenance": [
{
"source": "ghsa",
"kind": "map",
"value": "advisory",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"advisory"
]
},
{
"source": "stellaops-mirror",
"kind": "map",
"value": "domain=primary;repository=mirror-primary;generated=2025-10-19T12:00:00.0000000+00:00",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"advisory",
"credits[]",
"cvssmetrics[]",
"cwes[]",
"references[]"
]
}
],
"published": "2025-10-10T00:00:00+00:00",
"references": [
{
"kind": "advisory",
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "reference",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"references[]"
]
},
"sourceTag": "vendor",
"summary": "Vendor bulletin",
"url": "https://example.com/advisory"
}
],
"severity": "high",
"summary": "Upstream advisory replicated through StellaOps mirror.",
"title": "Sample Mirror Advisory"
}

View File

@@ -0,0 +1,202 @@
{
"advisories": [
{
"advisoryKey": "CVE-2025-1111",
"affectedPackages": [
{
"type": "semver",
"identifier": "pkg:npm/example@1.0.0",
"platform": null,
"versionRanges": [
{
"fixedVersion": "1.2.0",
"introducedVersion": "1.0.0",
"lastAffectedVersion": null,
"primitives": {
"evr": null,
"hasVendorExtensions": false,
"nevra": null,
"semVer": {
"constraintExpression": ">=1.0.0,<1.2.0",
"exactValue": null,
"fixed": "1.2.0",
"fixedInclusive": false,
"introduced": "1.0.0",
"introducedInclusive": true,
"lastAffected": null,
"lastAffectedInclusive": true,
"style": "range"
},
"vendorExtensions": null
},
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "range",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[].versionranges[]"
]
},
"rangeExpression": ">=1.0.0,<1.2.0",
"rangeKind": "semver"
}
],
"normalizedVersions": [
{
"scheme": "semver",
"type": "range",
"min": "1.0.0",
"minInclusive": true,
"max": "1.2.0",
"maxInclusive": false,
"value": null,
"notes": null
}
],
"statuses": [
{
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "status",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[].statuses[]"
]
},
"status": "fixed"
}
],
"provenance": [
{
"source": "ghsa",
"kind": "map",
"value": "package",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"affectedpackages[]"
]
}
]
}
],
"aliases": [
"GHSA-xxxx-xxxx-xxxx"
],
"canonicalMetricId": "cvss::ghsa::CVE-2025-1111",
"credits": [
{
"displayName": "Security Researcher",
"role": "reporter",
"contacts": [
"mailto:researcher@example.com"
],
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "credit",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"credits[]"
]
}
}
],
"cvssMetrics": [
{
"baseScore": 9.8,
"baseSeverity": "critical",
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "cvss",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"cvssmetrics[]"
]
},
"vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"version": "3.1"
}
],
"cwes": [
{
"taxonomy": "cwe",
"identifier": "CWE-79",
"name": "Cross-site Scripting",
"uri": "https://cwe.mitre.org/data/definitions/79.html",
"provenance": [
{
"source": "ghsa",
"kind": "map",
"value": "cwe",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"cwes[]"
]
}
]
}
],
"description": "Deterministic test payload distributed via mirror.",
"exploitKnown": false,
"language": "en",
"modified": "2025-10-11T00:00:00+00:00",
"provenance": [
{
"source": "ghsa",
"kind": "map",
"value": "advisory",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"advisory"
]
}
],
"published": "2025-10-10T00:00:00+00:00",
"references": [
{
"kind": "advisory",
"provenance": {
"source": "ghsa",
"kind": "map",
"value": "reference",
"decisionReason": null,
"recordedAt": "2025-10-19T12:00:00+00:00",
"fieldMask": [
"references[]"
]
},
"sourceTag": "vendor",
"summary": "Vendor bulletin",
"url": "https://example.com/advisory"
}
],
"severity": "high",
"summary": "Upstream advisory replicated through StellaOps mirror.",
"title": "Sample Mirror Advisory"
}
],
"advisoryCount": 1,
"displayName": "Primary Mirror",
"domainId": "primary",
"generatedAt": "2025-10-19T12:00:00+00:00",
"schemaVersion": 1,
"sources": [
{
"advisoryCount": 1,
"firstRecordedAt": "2025-10-19T12:00:00+00:00",
"lastRecordedAt": "2025-10-19T12:00:00+00:00",
"source": "ghsa"
}
],
"targetRepository": "mirror-primary"
}

View File

@@ -0,0 +1,47 @@
using System;
using StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
using StellaOps.Concelier.Models;
using Xunit;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
public sealed class MirrorAdvisoryMapperTests
{
[Fact]
public void Map_ProducesCanonicalAdvisoryWithMirrorProvenance()
{
var bundle = SampleData.CreateBundle();
var bundleJson = CanonicalJsonSerializer.SerializeIndented(bundle);
Assert.Equal(
FixtureLoader.Read(SampleData.BundleFixture).TrimEnd(),
FixtureLoader.Normalize(bundleJson).TrimEnd());
var advisories = MirrorAdvisoryMapper.Map(bundle);
Assert.Single(advisories);
var advisory = advisories[0];
var expectedAdvisory = SampleData.CreateExpectedMappedAdvisory();
var expectedJson = CanonicalJsonSerializer.SerializeIndented(expectedAdvisory);
Assert.Equal(
FixtureLoader.Read(SampleData.AdvisoryFixture).TrimEnd(),
FixtureLoader.Normalize(expectedJson).TrimEnd());
var actualJson = CanonicalJsonSerializer.SerializeIndented(advisory);
Assert.Equal(
FixtureLoader.Normalize(expectedJson).TrimEnd(),
FixtureLoader.Normalize(actualJson).TrimEnd());
Assert.Contains(advisory.Aliases, alias => string.Equals(alias, advisory.AdvisoryKey, StringComparison.OrdinalIgnoreCase));
Assert.Contains(
advisory.Provenance,
provenance => string.Equals(provenance.Source, StellaOpsMirrorConnector.Source, StringComparison.Ordinal) &&
string.Equals(provenance.Kind, "map", StringComparison.Ordinal));
var package = Assert.Single(advisory.AffectedPackages);
Assert.Contains(
package.Provenance,
provenance => string.Equals(provenance.Source, StellaOpsMirrorConnector.Source, StringComparison.Ordinal) &&
string.Equals(provenance.Kind, "map", StringComparison.Ordinal));
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Concelier.Connector.StellaOpsMirror.Security;
using StellaOps.Cryptography;
@@ -18,7 +20,7 @@ public sealed class MirrorSignatureVerifierTests
provider.UpsertSigningKey(key);
var registry = new CryptoProviderRegistry(new[] { provider });
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance);
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance, new MemoryCache(new MemoryCacheOptions()));
var payloadText = System.Text.Json.JsonSerializer.Serialize(new { advisories = Array.Empty<string>() });
var payload = payloadText.ToUtf8Bytes();
@@ -35,13 +37,13 @@ public sealed class MirrorSignatureVerifierTests
provider.UpsertSigningKey(key);
var registry = new CryptoProviderRegistry(new[] { provider });
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance);
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance, new MemoryCache(new MemoryCacheOptions()));
var payloadText = System.Text.Json.JsonSerializer.Serialize(new { advisories = Array.Empty<string>() });
var payload = payloadText.ToUtf8Bytes();
var (signature, _) = await CreateDetachedJwsAsync(provider, key.Reference.KeyId, payload);
var tampered = signature.Replace('a', 'b', StringComparison.Ordinal);
var tampered = signature.Replace('a', 'b');
await Assert.ThrowsAsync<InvalidOperationException>(() => verifier.VerifyAsync(payload, tampered, CancellationToken.None));
}
@@ -54,7 +56,7 @@ public sealed class MirrorSignatureVerifierTests
provider.UpsertSigningKey(key);
var registry = new CryptoProviderRegistry(new[] { provider });
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance);
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance, new MemoryCache(new MemoryCacheOptions()));
var payloadText = System.Text.Json.JsonSerializer.Serialize(new { advisories = Array.Empty<string>() });
var payload = payloadText.ToUtf8Bytes();
@@ -65,6 +67,7 @@ public sealed class MirrorSignatureVerifierTests
signature,
expectedKeyId: "unexpected-key",
expectedProvider: null,
fallbackPublicKeyPath: null,
cancellationToken: CancellationToken.None));
}
@@ -76,7 +79,7 @@ public sealed class MirrorSignatureVerifierTests
provider.UpsertSigningKey(key);
var registry = new CryptoProviderRegistry(new[] { provider });
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance);
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance, new MemoryCache(new MemoryCacheOptions()));
var payloadText = System.Text.Json.JsonSerializer.Serialize(new { advisories = Array.Empty<string>() });
var payload = payloadText.ToUtf8Bytes();
@@ -89,9 +92,42 @@ public sealed class MirrorSignatureVerifierTests
signature,
expectedKeyId: key.Reference.KeyId,
expectedProvider: provider.Name,
fallbackPublicKeyPath: null,
cancellationToken: CancellationToken.None));
}
[Fact]
public async Task VerifyAsync_UsesCachedPublicKeyWhenFileRemoved()
{
var provider = new DefaultCryptoProvider();
var signingKey = CreateSigningKey("mirror-key");
provider.UpsertSigningKey(signingKey);
var registry = new CryptoProviderRegistry(new[] { provider });
var memoryCache = new MemoryCache(new MemoryCacheOptions());
var verifier = new MirrorSignatureVerifier(registry, NullLogger<MirrorSignatureVerifier>.Instance, memoryCache);
var payload = "{\"advisories\":[]}";
var (signature, _) = await CreateDetachedJwsAsync(provider, signingKey.Reference.KeyId, payload.ToUtf8Bytes());
provider.RemoveSigningKey(signingKey.Reference.KeyId);
var pemPath = WritePublicKeyPem(signingKey);
try
{
await verifier.VerifyAsync(payload.ToUtf8Bytes(), signature, expectedKeyId: signingKey.Reference.KeyId, expectedProvider: "default", fallbackPublicKeyPath: pemPath, cancellationToken: CancellationToken.None);
File.Delete(pemPath);
await verifier.VerifyAsync(payload.ToUtf8Bytes(), signature, expectedKeyId: signingKey.Reference.KeyId, expectedProvider: "default", fallbackPublicKeyPath: pemPath, cancellationToken: CancellationToken.None);
}
finally
{
if (File.Exists(pemPath))
{
File.Delete(pemPath);
}
}
}
private static CryptoSigningKey CreateSigningKey(string keyId)
{
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
@@ -99,6 +135,16 @@ public sealed class MirrorSignatureVerifierTests
return new CryptoSigningKey(new CryptoKeyReference(keyId), SignatureAlgorithms.Es256, in parameters, DateTimeOffset.UtcNow);
}
private static string WritePublicKeyPem(CryptoSigningKey signingKey)
{
using var ecdsa = ECDsa.Create(signingKey.PublicParameters);
var info = ecdsa.ExportSubjectPublicKeyInfo();
var pem = PemEncoding.Write("PUBLIC KEY", info);
var path = Path.Combine(Path.GetTempPath(), $"stellaops-mirror-{Guid.NewGuid():N}.pem");
File.WriteAllText(path, pem);
return path;
}
private static async Task<(string Signature, DateTimeOffset SignedAt)> CreateDetachedJwsAsync(
DefaultCryptoProvider provider,
string keyId,

View File

@@ -0,0 +1,265 @@
using System;
using System.Globalization;
using StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
using StellaOps.Concelier.Models;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
internal static class SampleData
{
public const string BundleFixture = "mirror-bundle.sample.json";
public const string AdvisoryFixture = "mirror-advisory.expected.json";
public const string TargetRepository = "mirror-primary";
public const string DomainId = "primary";
public const string AdvisoryKey = "CVE-2025-1111";
public const string GhsaAlias = "GHSA-xxxx-xxxx-xxxx";
public static DateTimeOffset GeneratedAt { get; } = new(2025, 10, 19, 12, 0, 0, TimeSpan.Zero);
public static MirrorBundleDocument CreateBundle()
=> new(
SchemaVersion: 1,
GeneratedAt: GeneratedAt,
TargetRepository: TargetRepository,
DomainId: DomainId,
DisplayName: "Primary Mirror",
AdvisoryCount: 1,
Advisories: new[] { CreateSourceAdvisory() },
Sources: new[]
{
new MirrorSourceSummary("ghsa", GeneratedAt, GeneratedAt, 1)
});
public static Advisory CreateExpectedMappedAdvisory()
{
var baseAdvisory = CreateSourceAdvisory();
var recordedAt = GeneratedAt.ToUniversalTime();
var mirrorValue = BuildMirrorValue(recordedAt);
var topProvenance = baseAdvisory.Provenance.Add(new AdvisoryProvenance(
StellaOpsMirrorConnector.Source,
"map",
mirrorValue,
recordedAt,
new[]
{
ProvenanceFieldMasks.Advisory,
ProvenanceFieldMasks.References,
ProvenanceFieldMasks.Credits,
ProvenanceFieldMasks.CvssMetrics,
ProvenanceFieldMasks.Weaknesses,
}));
var package = baseAdvisory.AffectedPackages[0];
var packageProvenance = package.Provenance.Add(new AdvisoryProvenance(
StellaOpsMirrorConnector.Source,
"map",
$"{mirrorValue};package={package.Identifier}",
recordedAt,
new[]
{
ProvenanceFieldMasks.AffectedPackages,
ProvenanceFieldMasks.VersionRanges,
ProvenanceFieldMasks.PackageStatuses,
ProvenanceFieldMasks.NormalizedVersions,
}));
var updatedPackage = new AffectedPackage(
package.Type,
package.Identifier,
package.Platform,
package.VersionRanges,
package.Statuses,
packageProvenance,
package.NormalizedVersions);
return new Advisory(
AdvisoryKey,
baseAdvisory.Title,
baseAdvisory.Summary,
baseAdvisory.Language,
baseAdvisory.Published,
baseAdvisory.Modified,
baseAdvisory.Severity,
baseAdvisory.ExploitKnown,
new[] { AdvisoryKey, GhsaAlias },
baseAdvisory.Credits,
baseAdvisory.References,
new[] { updatedPackage },
baseAdvisory.CvssMetrics,
topProvenance,
baseAdvisory.Description,
baseAdvisory.Cwes,
baseAdvisory.CanonicalMetricId);
}
private static Advisory CreateSourceAdvisory()
{
var recordedAt = GeneratedAt.ToUniversalTime();
var reference = new AdvisoryReference(
"https://example.com/advisory",
"advisory",
"vendor",
"Vendor bulletin",
new AdvisoryProvenance(
"ghsa",
"map",
"reference",
recordedAt,
new[]
{
ProvenanceFieldMasks.References,
}));
var credit = new AdvisoryCredit(
"Security Researcher",
"reporter",
new[] { "mailto:researcher@example.com" },
new AdvisoryProvenance(
"ghsa",
"map",
"credit",
recordedAt,
new[]
{
ProvenanceFieldMasks.Credits,
}));
var semVerPrimitive = new SemVerPrimitive(
Introduced: "1.0.0",
IntroducedInclusive: true,
Fixed: "1.2.0",
FixedInclusive: false,
LastAffected: null,
LastAffectedInclusive: true,
ConstraintExpression: ">=1.0.0,<1.2.0",
ExactValue: null);
var range = new AffectedVersionRange(
rangeKind: "semver",
introducedVersion: "1.0.0",
fixedVersion: "1.2.0",
lastAffectedVersion: null,
rangeExpression: ">=1.0.0,<1.2.0",
provenance: new AdvisoryProvenance(
"ghsa",
"map",
"range",
recordedAt,
new[]
{
ProvenanceFieldMasks.VersionRanges,
}),
primitives: new RangePrimitives(semVerPrimitive, null, null, null));
var status = new AffectedPackageStatus(
"fixed",
new AdvisoryProvenance(
"ghsa",
"map",
"status",
recordedAt,
new[]
{
ProvenanceFieldMasks.PackageStatuses,
}));
var normalizedRule = new NormalizedVersionRule(
scheme: "semver",
type: "range",
min: "1.0.0",
minInclusive: true,
max: "1.2.0",
maxInclusive: false,
value: null,
notes: null);
var package = new AffectedPackage(
AffectedPackageTypes.SemVer,
"pkg:npm/example@1.0.0",
platform: null,
versionRanges: new[] { range },
statuses: new[] { status },
provenance: new[]
{
new AdvisoryProvenance(
"ghsa",
"map",
"package",
recordedAt,
new[]
{
ProvenanceFieldMasks.AffectedPackages,
})
},
normalizedVersions: new[] { normalizedRule });
var cvss = new CvssMetric(
"3.1",
"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
9.8,
"critical",
new AdvisoryProvenance(
"ghsa",
"map",
"cvss",
recordedAt,
new[]
{
ProvenanceFieldMasks.CvssMetrics,
}));
var weakness = new AdvisoryWeakness(
"cwe",
"CWE-79",
"Cross-site Scripting",
"https://cwe.mitre.org/data/definitions/79.html",
new[]
{
new AdvisoryProvenance(
"ghsa",
"map",
"cwe",
recordedAt,
new[]
{
ProvenanceFieldMasks.Weaknesses,
})
});
var advisory = new Advisory(
AdvisoryKey,
"Sample Mirror Advisory",
"Upstream advisory replicated through StellaOps mirror.",
"en",
published: new DateTimeOffset(2025, 10, 10, 0, 0, 0, TimeSpan.Zero),
modified: new DateTimeOffset(2025, 10, 11, 0, 0, 0, TimeSpan.Zero),
severity: "high",
exploitKnown: false,
aliases: new[] { GhsaAlias },
credits: new[] { credit },
references: new[] { reference },
affectedPackages: new[] { package },
cvssMetrics: new[] { cvss },
provenance: new[]
{
new AdvisoryProvenance(
"ghsa",
"map",
"advisory",
recordedAt,
new[]
{
ProvenanceFieldMasks.Advisory,
})
},
description: "Deterministic test payload distributed via mirror.",
cwes: new[] { weakness },
canonicalMetricId: "cvss::ghsa::CVE-2025-1111");
return CanonicalJsonSerializer.Normalize(advisory);
}
private static string BuildMirrorValue(DateTimeOffset recordedAt)
=> $"domain={DomainId};repository={TargetRepository};generated={recordedAt.ToString("O", CultureInfo.InvariantCulture)}";
}

View File

@@ -4,8 +4,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj" />
<ProjectReference Include="../StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
</ItemGroup>
</Project>
<ItemGroup>
<ProjectReference Include="../StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj" />
<ProjectReference Include="../StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="Fixtures\**\*.json" CopyToOutputDirectory="Always" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
@@ -15,11 +16,15 @@ using MongoDB.Bson;
using StellaOps.Concelier.Connector.Common;
using StellaOps.Concelier.Connector.Common.Fetch;
using StellaOps.Concelier.Connector.Common.Testing;
using StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
using StellaOps.Concelier.Connector.StellaOpsMirror.Settings;
using StellaOps.Concelier.Storage.Mongo;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Mongo.Documents;
using StellaOps.Concelier.Storage.Mongo.Dtos;
using StellaOps.Concelier.Testing;
using StellaOps.Cryptography;
using StellaOps.Concelier.Models;
using Xunit;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
@@ -168,6 +173,95 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
await Assert.ThrowsAsync<InvalidOperationException>(() => connector.FetchAsync(provider, CancellationToken.None));
}
[Fact]
public async Task FetchAsync_VerifiesSignatureUsingFallbackPublicKey()
{
var manifestContent = "{\"domain\":\"primary\"}";
var bundleContent = "{\"advisories\":[{\"id\":\"CVE-2025-0004\"}]}";
var manifestDigest = ComputeDigest(manifestContent);
var bundleDigest = ComputeDigest(bundleContent);
var index = BuildIndex(manifestDigest, Encoding.UTF8.GetByteCount(manifestContent), bundleDigest, Encoding.UTF8.GetByteCount(bundleContent), includeSignature: true);
var signingKey = CreateSigningKey("mirror-key");
var (signatureValue, _) = CreateDetachedJws(signingKey, bundleContent);
var publicKeyPath = WritePublicKeyPem(signingKey);
await using var provider = await BuildServiceProviderAsync(options =>
{
options.Signature.Enabled = true;
options.Signature.KeyId = "mirror-key";
options.Signature.Provider = "default";
options.Signature.PublicKeyPath = publicKeyPath;
});
try
{
SeedResponses(index, manifestContent, bundleContent, signatureValue);
var connector = provider.GetRequiredService<StellaOpsMirrorConnector>();
await connector.FetchAsync(provider, CancellationToken.None);
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(StellaOpsMirrorConnector.Source, CancellationToken.None);
Assert.NotNull(state);
Assert.Equal(0, state!.FailCount);
}
finally
{
if (File.Exists(publicKeyPath))
{
File.Delete(publicKeyPath);
}
}
}
[Fact]
public async Task FetchAsync_DigestMismatchMarksFailure()
{
var manifestExpected = "{\"domain\":\"primary\"}";
var manifestTampered = "{\"domain\":\"tampered\"}";
var bundleContent = "{\"advisories\":[{\"id\":\"CVE-2025-0005\"}]}";
var manifestDigest = ComputeDigest(manifestExpected);
var bundleDigest = ComputeDigest(bundleContent);
var index = BuildIndex(manifestDigest, Encoding.UTF8.GetByteCount(manifestExpected), bundleDigest, Encoding.UTF8.GetByteCount(bundleContent), includeSignature: false);
await using var provider = await BuildServiceProviderAsync();
SeedResponses(index, manifestTampered, bundleContent, signature: null);
var connector = provider.GetRequiredService<StellaOpsMirrorConnector>();
await Assert.ThrowsAsync<InvalidOperationException>(() => connector.FetchAsync(provider, CancellationToken.None));
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(StellaOpsMirrorConnector.Source, CancellationToken.None);
Assert.NotNull(state);
var cursor = state!.Cursor ?? new BsonDocument();
Assert.True(state.FailCount >= 1);
Assert.False(cursor.Contains("bundleDigest"));
}
[Fact]
public void ParseAndMap_PersistAdvisoriesFromBundle()
{
var bundleDocument = SampleData.CreateBundle();
var bundleJson = CanonicalJsonSerializer.SerializeIndented(bundleDocument);
var normalizedFixture = FixtureLoader.Read(SampleData.BundleFixture).TrimEnd();
Assert.Equal(normalizedFixture, FixtureLoader.Normalize(bundleJson).TrimEnd());
var advisories = MirrorAdvisoryMapper.Map(bundleDocument);
Assert.Single(advisories);
var advisory = advisories[0];
var expectedAdvisoryJson = FixtureLoader.Read(SampleData.AdvisoryFixture).TrimEnd();
var mappedJson = CanonicalJsonSerializer.SerializeIndented(advisory);
Assert.Equal(expectedAdvisoryJson, FixtureLoader.Normalize(mappedJson).TrimEnd());
// AdvisoryStore integration validated elsewhere; ensure canonical serialization is stable.
}
public Task InitializeAsync() => Task.CompletedTask;
public Task DisposeAsync()
@@ -323,6 +417,17 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
return new CryptoSigningKey(new CryptoKeyReference(keyId), SignatureAlgorithms.Es256, in parameters, DateTimeOffset.UtcNow);
}
private static string WritePublicKeyPem(CryptoSigningKey signingKey)
{
ArgumentNullException.ThrowIfNull(signingKey);
var path = Path.Combine(Path.GetTempPath(), $"stellaops-mirror-{Guid.NewGuid():N}.pem");
using var ecdsa = ECDsa.Create(signingKey.PublicParameters);
var publicKeyInfo = ecdsa.ExportSubjectPublicKeyInfo();
var pem = PemEncoding.Write("PUBLIC KEY", publicKeyInfo);
File.WriteAllText(path, pem);
return path;
}
private static (string Signature, DateTimeOffset SignedAt) CreateDetachedJws(CryptoSigningKey signingKey, string payload)
{
var provider = new DefaultCryptoProvider();

View File

@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Globalization;
using StellaOps.Concelier.Models;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
internal static class MirrorAdvisoryMapper
{
private const string MirrorProvenanceKind = "map";
private static readonly string[] TopLevelFieldMask =
{
ProvenanceFieldMasks.Advisory,
ProvenanceFieldMasks.References,
ProvenanceFieldMasks.Credits,
ProvenanceFieldMasks.CvssMetrics,
ProvenanceFieldMasks.Weaknesses,
};
public static ImmutableArray<Advisory> Map(MirrorBundleDocument bundle)
{
if (bundle?.Advisories is null || bundle.Advisories.Count == 0)
{
return ImmutableArray<Advisory>.Empty;
}
var builder = ImmutableArray.CreateBuilder<Advisory>(bundle.Advisories.Count);
var recordedAt = bundle.GeneratedAt.ToUniversalTime();
var mirrorValue = BuildMirrorValue(bundle, recordedAt);
var topLevelProvenance = new AdvisoryProvenance(
StellaOpsMirrorConnector.Source,
MirrorProvenanceKind,
mirrorValue,
recordedAt,
TopLevelFieldMask);
foreach (var advisory in bundle.Advisories)
{
if (advisory is null)
{
continue;
}
var normalized = CanonicalJsonSerializer.Normalize(advisory);
var aliases = EnsureAliasCoverage(normalized);
var provenance = EnsureProvenance(normalized.Provenance, topLevelProvenance);
var packages = EnsurePackageProvenance(normalized.AffectedPackages, mirrorValue, recordedAt);
var updated = new Advisory(
normalized.AdvisoryKey,
normalized.Title,
normalized.Summary,
normalized.Language,
normalized.Published,
normalized.Modified,
normalized.Severity,
normalized.ExploitKnown,
aliases,
normalized.Credits,
normalized.References,
packages,
normalized.CvssMetrics,
provenance,
normalized.Description,
normalized.Cwes,
normalized.CanonicalMetricId);
builder.Add(updated);
}
return builder.ToImmutable();
}
private static IEnumerable<string> EnsureAliasCoverage(Advisory advisory)
{
var aliases = new List<string>(advisory.Aliases.Length + 1);
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var alias in advisory.Aliases)
{
if (seen.Add(alias))
{
aliases.Add(alias);
}
}
if (seen.Add(advisory.AdvisoryKey))
{
aliases.Add(advisory.AdvisoryKey);
}
return aliases;
}
private static IEnumerable<AdvisoryProvenance> EnsureProvenance(
ImmutableArray<AdvisoryProvenance> existing,
AdvisoryProvenance mirrorProvenance)
{
if (!existing.IsDefaultOrEmpty
&& existing.Any(provenance =>
string.Equals(provenance.Source, mirrorProvenance.Source, StringComparison.Ordinal)
&& string.Equals(provenance.Kind, mirrorProvenance.Kind, StringComparison.Ordinal)
&& string.Equals(provenance.Value, mirrorProvenance.Value, StringComparison.Ordinal)))
{
return existing;
}
return existing.Add(mirrorProvenance);
}
private static IEnumerable<AffectedPackage> EnsurePackageProvenance(
ImmutableArray<AffectedPackage> packages,
string mirrorValue,
DateTimeOffset recordedAt)
{
if (packages.IsDefaultOrEmpty || packages.Length == 0)
{
return packages;
}
var results = new List<AffectedPackage>(packages.Length);
foreach (var package in packages)
{
var value = $"{mirrorValue};package={package.Identifier}";
if (!package.Provenance.IsDefaultOrEmpty
&& package.Provenance.Any(provenance =>
string.Equals(provenance.Source, StellaOpsMirrorConnector.Source, StringComparison.Ordinal)
&& string.Equals(provenance.Kind, MirrorProvenanceKind, StringComparison.Ordinal)
&& string.Equals(provenance.Value, value, StringComparison.Ordinal)))
{
results.Add(package);
continue;
}
var masks = BuildPackageFieldMask(package);
var packageProvenance = new AdvisoryProvenance(
StellaOpsMirrorConnector.Source,
MirrorProvenanceKind,
value,
recordedAt,
masks);
var provenance = package.Provenance.Add(packageProvenance);
var updated = new AffectedPackage(
package.Type,
package.Identifier,
package.Platform,
package.VersionRanges,
package.Statuses,
provenance,
package.NormalizedVersions);
results.Add(updated);
}
return results;
}
private static string[] BuildPackageFieldMask(AffectedPackage package)
{
var masks = new HashSet<string>(StringComparer.Ordinal)
{
ProvenanceFieldMasks.AffectedPackages,
};
if (!package.VersionRanges.IsDefaultOrEmpty && package.VersionRanges.Length > 0)
{
masks.Add(ProvenanceFieldMasks.VersionRanges);
}
if (!package.Statuses.IsDefaultOrEmpty && package.Statuses.Length > 0)
{
masks.Add(ProvenanceFieldMasks.PackageStatuses);
}
if (!package.NormalizedVersions.IsDefaultOrEmpty && package.NormalizedVersions.Length > 0)
{
masks.Add(ProvenanceFieldMasks.NormalizedVersions);
}
return masks.ToArray();
}
private static string BuildMirrorValue(MirrorBundleDocument bundle, DateTimeOffset recordedAt)
{
var segments = new List<string>
{
$"domain={bundle.DomainId}",
};
if (!string.IsNullOrWhiteSpace(bundle.TargetRepository))
{
segments.Add($"repository={bundle.TargetRepository}");
}
segments.Add($"generated={recordedAt.ToString("O", CultureInfo.InvariantCulture)}");
return string.Join(';', segments);
}
}

View File

@@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
using StellaOps.Concelier.Models;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
public sealed record MirrorBundleDocument(
[property: JsonPropertyName("schemaVersion")] int SchemaVersion,
[property: JsonPropertyName("generatedAt")] DateTimeOffset GeneratedAt,
[property: JsonPropertyName("targetRepository")] string? TargetRepository,
[property: JsonPropertyName("domainId")] string DomainId,
[property: JsonPropertyName("displayName")] string DisplayName,
[property: JsonPropertyName("advisoryCount")] int AdvisoryCount,
[property: JsonPropertyName("advisories")] IReadOnlyList<Advisory> Advisories,
[property: JsonPropertyName("sources")] IReadOnlyList<MirrorSourceSummary> Sources);

View File

@@ -3,93 +3,107 @@ using MongoDB.Bson;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
internal sealed record StellaOpsMirrorCursor(
string? ExportId,
string? BundleDigest,
DateTimeOffset? GeneratedAt,
IReadOnlyCollection<Guid> PendingDocuments,
IReadOnlyCollection<Guid> PendingMappings)
{
private static readonly IReadOnlyCollection<Guid> EmptyGuids = Array.Empty<Guid>();
public static StellaOpsMirrorCursor Empty { get; } = new(
ExportId: null,
BundleDigest: null,
GeneratedAt: null,
PendingDocuments: EmptyGuids,
PendingMappings: EmptyGuids);
public BsonDocument ToBsonDocument()
{
var document = new BsonDocument
{
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
};
if (!string.IsNullOrWhiteSpace(ExportId))
{
document["exportId"] = ExportId;
}
if (!string.IsNullOrWhiteSpace(BundleDigest))
{
document["bundleDigest"] = BundleDigest;
}
if (GeneratedAt.HasValue)
{
document["generatedAt"] = GeneratedAt.Value.UtcDateTime;
}
return document;
}
public static StellaOpsMirrorCursor FromBson(BsonDocument? document)
internal sealed record StellaOpsMirrorCursor(
string? ExportId,
string? BundleDigest,
DateTimeOffset? GeneratedAt,
IReadOnlyCollection<Guid> PendingDocuments,
IReadOnlyCollection<Guid> PendingMappings,
string? CompletedFingerprint)
{
private static readonly IReadOnlyCollection<Guid> EmptyGuids = Array.Empty<Guid>();
public static StellaOpsMirrorCursor Empty { get; } = new(
ExportId: null,
BundleDigest: null,
GeneratedAt: null,
PendingDocuments: EmptyGuids,
PendingMappings: EmptyGuids,
CompletedFingerprint: null);
public BsonDocument ToBsonDocument()
{
var document = new BsonDocument
{
["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
};
if (!string.IsNullOrWhiteSpace(ExportId))
{
document["exportId"] = ExportId;
}
if (!string.IsNullOrWhiteSpace(BundleDigest))
{
document["bundleDigest"] = BundleDigest;
}
if (GeneratedAt.HasValue)
{
document["generatedAt"] = GeneratedAt.Value.UtcDateTime;
}
if (!string.IsNullOrWhiteSpace(CompletedFingerprint))
{
document["completedFingerprint"] = CompletedFingerprint;
}
return document;
}
public static StellaOpsMirrorCursor FromBson(BsonDocument? document)
{
if (document is null || document.ElementCount == 0)
{
return Empty;
}
var exportId = document.TryGetValue("exportId", out var exportValue) && exportValue.IsString ? exportValue.AsString : null;
var digest = document.TryGetValue("bundleDigest", out var digestValue) && digestValue.IsString ? digestValue.AsString : null;
DateTimeOffset? generatedAt = null;
if (document.TryGetValue("generatedAt", out var generatedValue))
{
generatedAt = generatedValue.BsonType switch
{
var exportId = document.TryGetValue("exportId", out var exportValue) && exportValue.IsString ? exportValue.AsString : null;
var digest = document.TryGetValue("bundleDigest", out var digestValue) && digestValue.IsString ? digestValue.AsString : null;
DateTimeOffset? generatedAt = null;
if (document.TryGetValue("generatedAt", out var generatedValue))
{
generatedAt = generatedValue.BsonType switch
{
BsonType.DateTime => DateTime.SpecifyKind(generatedValue.ToUniversalTime(), DateTimeKind.Utc),
BsonType.String when DateTimeOffset.TryParse(generatedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
_ => null,
};
}
var pendingDocuments = ReadGuidArray(document, "pendingDocuments");
var pendingMappings = ReadGuidArray(document, "pendingMappings");
return new StellaOpsMirrorCursor(exportId, digest, generatedAt, pendingDocuments, pendingMappings);
}
public StellaOpsMirrorCursor WithPendingDocuments(IEnumerable<Guid> documents)
=> this with { PendingDocuments = documents?.Distinct().ToArray() ?? EmptyGuids };
public StellaOpsMirrorCursor WithPendingMappings(IEnumerable<Guid> mappings)
=> this with { PendingMappings = mappings?.Distinct().ToArray() ?? EmptyGuids };
public StellaOpsMirrorCursor WithBundleSnapshot(string? exportId, string? digest, DateTimeOffset generatedAt)
=> this with
{
ExportId = string.IsNullOrWhiteSpace(exportId) ? ExportId : exportId,
BundleDigest = digest,
GeneratedAt = generatedAt,
};
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
{
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
{
return EmptyGuids;
_ => null,
};
}
var pendingDocuments = ReadGuidArray(document, "pendingDocuments");
var pendingMappings = ReadGuidArray(document, "pendingMappings");
var fingerprint = document.TryGetValue("completedFingerprint", out var fingerprintValue) && fingerprintValue.IsString
? fingerprintValue.AsString
: null;
return new StellaOpsMirrorCursor(exportId, digest, generatedAt, pendingDocuments, pendingMappings, fingerprint);
}
public StellaOpsMirrorCursor WithPendingDocuments(IEnumerable<Guid> documents)
=> this with { PendingDocuments = documents?.Distinct().ToArray() ?? EmptyGuids };
public StellaOpsMirrorCursor WithPendingMappings(IEnumerable<Guid> mappings)
=> this with { PendingMappings = mappings?.Distinct().ToArray() ?? EmptyGuids };
public StellaOpsMirrorCursor WithBundleSnapshot(string? exportId, string? digest, DateTimeOffset generatedAt)
=> this with
{
ExportId = string.IsNullOrWhiteSpace(exportId) ? ExportId : exportId,
BundleDigest = digest,
GeneratedAt = generatedAt,
};
public StellaOpsMirrorCursor WithCompletedFingerprint(string? fingerprint)
=> this with { CompletedFingerprint = string.IsNullOrWhiteSpace(fingerprint) ? null : fingerprint };
private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
{
if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
{
return EmptyGuids;
}
var results = new List<Guid>(array.Count);

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("StellaOps.Concelier.Connector.StellaOpsMirror.Tests")]

Some files were not shown because too many files have changed in this diff Show More