Add impact index fixture and filesystem artifact uploader
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Introduced a sample BOM index JSON file for impact index testing.
- Created unit tests for the impact index fixture to ensure proper loading of sample images.
- Implemented the FilesystemPackRunArtifactUploader class to handle artifact uploads to the local filesystem.
- Added comprehensive tests for the FilesystemPackRunArtifactUploader, covering file copying, missing files, and expression outputs.
This commit is contained in:
master
2025-11-06 09:52:16 +02:00
parent dd217b4546
commit 822e3b6037
45 changed files with 1358 additions and 746 deletions

View File

@@ -23,13 +23,27 @@ This directory contains deterministic deployment bundles for the core Stella Ops
`python ./ops/devops/telemetry/smoke_otel_collector.py` to verify the OTLP endpoints. `python ./ops/devops/telemetry/smoke_otel_collector.py` to verify the OTLP endpoints.
5. Commit the change alongside any documentation updates (e.g. install guide cross-links). 5. 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.
### Additional tooling ### Surface.Env rollout warnings
- `deploy/tools/check-channel-alignment.py` verifies that Helm/Compose profiles reference the exact images listed in a release manifest. Run it for each channel before promoting a release. - Compose (`deploy/compose/env/*.env.example`) and Helm (`deploy/helm/stellaops/values-*.yaml`) now seed `SCANNER_SURFACE_*` variables so the worker and web service resolve cache roots, Surface.FS endpoints, and secrets providers through `StellaOps.Scanner.Surface.Env`.
- `ops/devops/telemetry/generate_dev_tls.sh` produces local CA/server/client certificates for Compose-based collector testing. - During rollout, watch for structured log messages (and readiness output) prefixed with `surface.env.`—for example, `surface.env.cache_root_missing`, `surface.env.endpoint_unreachable`, or `surface.env.secrets_provider_invalid`.
- `ops/devops/telemetry/smoke_otel_collector.py` sends OTLP traffic and asserts the collector accepted traces, metrics, and logs. - Treat these warnings as deployment blockers: update the endpoint/cache/secrets values or permissions before promoting the environment, otherwise workers will fail fast at startup.
- Air-gapped bundles default the secrets provider to `file` with `/etc/stellaops/secrets`; connected clusters default to `kubernetes`. Adjust the provider/root pair if your secrets manager differs.
### Mongo2Go OpenSSL prerequisites
- Linux runners that execute Mongo2Go-backed suites (Excititor, Scheduler, Graph, etc.) must expose OpenSSL 1.1 (`libcrypto.so.1.1`, `libssl.so.1.1`). The canonical copies live under `tests/native/openssl-1.1/linux-x64`.
- Export `LD_LIBRARY_PATH="$(git rev-parse --show-toplevel)/tests/native/openssl-1.1/linux-x64:${LD_LIBRARY_PATH:-}"` before invoking `dotnet test`. Example:\
`LD_LIBRARY_PATH="$(pwd)/tests/native/openssl-1.1/linux-x64" dotnet test src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/StellaOps.Excititor.WebService.Tests.csproj --nologo`.
- CI agents or Dockerfiles that host these tests should either mount the directory into the container or copy the two `.so` files into a directory that is already on the runtime library path.
### Additional tooling
- `deploy/tools/check-channel-alignment.py` verifies that Helm/Compose profiles reference the exact images listed in a release manifest. Run it for each channel before promoting a release.
- `ops/devops/telemetry/generate_dev_tls.sh` produces local CA/server/client certificates for Compose-based collector testing.
- `ops/devops/telemetry/smoke_otel_collector.py` sends OTLP traffic and asserts the collector accepted traces, metrics, and logs.
- `ops/devops/telemetry/package_offline_bundle.py` packages telemetry assets (config/Helm/Compose) into a signed tarball for air-gapped installs. - `ops/devops/telemetry/package_offline_bundle.py` packages telemetry assets (config/Helm/Compose) into a signed tarball for air-gapped installs.
- `docs/modules/devops/runbooks/deployment-upgrade.md` end-to-end instructions for upgrade, rollback, and channel promotion workflows (Helm + Compose). - `docs/modules/devops/runbooks/deployment-upgrade.md` end-to-end instructions for upgrade, rollback, and channel promotion workflows (Helm + Compose).

View File

@@ -27,6 +27,10 @@ SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5 SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000 SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080/api/v1
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
SCANNER_SURFACE_SECRETS_PROVIDER=file
SCANNER_SURFACE_SECRETS_ROOT=/etc/stellaops/secrets
SCHEDULER_QUEUE_KIND=Nats SCHEDULER_QUEUE_KIND=Nats
SCHEDULER_QUEUE_NATS_URL=nats://nats:4222 SCHEDULER_QUEUE_NATS_URL=nats://nats:4222
SCHEDULER_STORAGE_DATABASE=stellaops_scheduler SCHEDULER_STORAGE_DATABASE=stellaops_scheduler

View File

@@ -26,6 +26,11 @@ SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5 SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000 SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
# Surface.Env defaults keep worker/web service aligned with local RustFS and inline secrets.
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080/api/v1
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
SCANNER_SURFACE_SECRETS_PROVIDER=inline
SCANNER_SURFACE_SECRETS_ROOT=
SCHEDULER_QUEUE_KIND=Nats SCHEDULER_QUEUE_KIND=Nats
SCHEDULER_QUEUE_NATS_URL=nats://nats:4222 SCHEDULER_QUEUE_NATS_URL=nats://nats:4222
SCHEDULER_STORAGE_DATABASE=stellaops_scheduler SCHEDULER_STORAGE_DATABASE=stellaops_scheduler

View File

@@ -6,10 +6,16 @@ MONGO_INITDB_ROOT_PASSWORD=mirror-password
MINIO_ROOT_USER=stellaops-mirror MINIO_ROOT_USER=stellaops-mirror
MINIO_ROOT_PASSWORD=mirror-minio-secret MINIO_ROOT_PASSWORD=mirror-minio-secret
RUSTFS_HTTP_PORT=8080 RUSTFS_HTTP_PORT=8080
# Mirror HTTP listeners # Scanner surface integration
MIRROR_GATEWAY_HTTP_PORT=8080 SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080/api/v1
MIRROR_GATEWAY_HTTPS_PORT=9443 SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
SCANNER_SURFACE_SECRETS_PROVIDER=file
SCANNER_SURFACE_SECRETS_ROOT=/etc/stellaops/secrets
# Mirror HTTP listeners
MIRROR_GATEWAY_HTTP_PORT=8080
MIRROR_GATEWAY_HTTPS_PORT=9443
# Concelier mirror configuration # Concelier mirror configuration
CONCELIER_MIRROR_LATEST_SEGMENT=latest CONCELIER_MIRROR_LATEST_SEGMENT=latest

View File

@@ -26,9 +26,13 @@ SCANNER_EVENTS_ENABLED=true
SCANNER_EVENTS_DRIVER=redis SCANNER_EVENTS_DRIVER=redis
# Leave SCANNER_EVENTS_DSN empty to inherit the Redis queue DSN when SCANNER_QUEUE_BROKER uses redis://. # Leave SCANNER_EVENTS_DSN empty to inherit the Redis queue DSN when SCANNER_QUEUE_BROKER uses redis://.
SCANNER_EVENTS_DSN= SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5 SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000 SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
SCANNER_SURFACE_FS_ENDPOINT=https://surfacefs.prod.stella-ops.org/api/v1
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
SCANNER_SURFACE_SECRETS_PROVIDER=kubernetes
SCANNER_SURFACE_SECRETS_ROOT=stellaops/scanner
SCHEDULER_QUEUE_KIND=Nats SCHEDULER_QUEUE_KIND=Nats
SCHEDULER_QUEUE_NATS_URL=nats://nats:4222 SCHEDULER_QUEUE_NATS_URL=nats://nats:4222
SCHEDULER_STORAGE_DATABASE=stellaops_scheduler SCHEDULER_STORAGE_DATABASE=stellaops_scheduler

View File

@@ -26,6 +26,10 @@ SCANNER_EVENTS_DSN=
SCANNER_EVENTS_STREAM=stella.events SCANNER_EVENTS_STREAM=stella.events
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5 SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000 SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080/api/v1
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
SCANNER_SURFACE_SECRETS_PROVIDER=kubernetes
SCANNER_SURFACE_SECRETS_ROOT=stellaops/scanner
SCHEDULER_QUEUE_KIND=Nats SCHEDULER_QUEUE_KIND=Nats
SCHEDULER_QUEUE_NATS_URL=nats://nats:4222 SCHEDULER_QUEUE_NATS_URL=nats://nats:4222
SCHEDULER_STORAGE_DATABASE=stellaops_scheduler SCHEDULER_STORAGE_DATABASE=stellaops_scheduler

View File

@@ -1,102 +1,102 @@
global: global:
profile: airgap profile: airgap
release: release:
version: "2025.09.2-airgap" version: "2025.09.2-airgap"
channel: airgap channel: airgap
manifestSha256: "b787b833dddd73960c31338279daa0b0a0dce2ef32bd32ef1aaf953d66135f94" manifestSha256: "b787b833dddd73960c31338279daa0b0a0dce2ef32bd32ef1aaf953d66135f94"
image: image:
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
labels: labels:
stellaops.io/channel: airgap stellaops.io/channel: airgap
configMaps: configMaps:
notify-config: notify-config:
data: data:
notify.yaml: | notify.yaml: |
storage: storage:
driver: mongo driver: mongo
connectionString: "mongodb://notify-mongo.prod.svc.cluster.local:27017" connectionString: "mongodb://notify-mongo.prod.svc.cluster.local:27017"
database: "stellaops_notify" database: "stellaops_notify"
commandTimeoutSeconds: 60 commandTimeoutSeconds: 60
authority: authority:
enabled: true enabled: true
issuer: "https://authority.stella-ops.org" issuer: "https://authority.stella-ops.org"
metadataAddress: "https://authority.stella-ops.org/.well-known/openid-configuration" metadataAddress: "https://authority.stella-ops.org/.well-known/openid-configuration"
requireHttpsMetadata: true requireHttpsMetadata: true
allowAnonymousFallback: false allowAnonymousFallback: false
backchannelTimeoutSeconds: 30 backchannelTimeoutSeconds: 30
tokenClockSkewSeconds: 60 tokenClockSkewSeconds: 60
audiences: audiences:
- notify - notify
readScope: notify.read readScope: notify.read
adminScope: notify.admin adminScope: notify.admin
api: api:
basePath: "/api/v1/notify" basePath: "/api/v1/notify"
internalBasePath: "/internal/notify" internalBasePath: "/internal/notify"
tenantHeader: "X-StellaOps-Tenant" tenantHeader: "X-StellaOps-Tenant"
plugins: plugins:
baseDirectory: "/var/opt/stellaops" baseDirectory: "/var/opt/stellaops"
directory: "plugins/notify" directory: "plugins/notify"
searchPatterns: searchPatterns:
- "StellaOps.Notify.Connectors.*.dll" - "StellaOps.Notify.Connectors.*.dll"
orderedPlugins: orderedPlugins:
- StellaOps.Notify.Connectors.Slack - StellaOps.Notify.Connectors.Slack
- StellaOps.Notify.Connectors.Teams - StellaOps.Notify.Connectors.Teams
- StellaOps.Notify.Connectors.Email - StellaOps.Notify.Connectors.Email
- StellaOps.Notify.Connectors.Webhook - StellaOps.Notify.Connectors.Webhook
telemetry: telemetry:
enableRequestLogging: true enableRequestLogging: true
minimumLogLevel: Warning minimumLogLevel: Warning
services: services:
authority: authority:
image: registry.stella-ops.org/stellaops/authority@sha256:5551a3269b7008cd5aceecf45df018c67459ed519557ccbe48b093b926a39bcc image: registry.stella-ops.org/stellaops/authority@sha256:5551a3269b7008cd5aceecf45df018c67459ed519557ccbe48b093b926a39bcc
service: service:
port: 8440 port: 8440
env: env:
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440" STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
STELLAOPS_AUTHORITY__ALLOWANONYMOUSFALLBACK: "false" STELLAOPS_AUTHORITY__ALLOWANONYMOUSFALLBACK: "false"
signer: signer:
image: registry.stella-ops.org/stellaops/signer@sha256:ddbbd664a42846cea6b40fca6465bc679b30f72851158f300d01a8571c5478fc image: registry.stella-ops.org/stellaops/signer@sha256:ddbbd664a42846cea6b40fca6465bc679b30f72851158f300d01a8571c5478fc
service: service:
port: 8441 port: 8441
env: env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "file:///offline/poe/introspect.json" SIGNER__POE__INTROSPECTURL: "file:///offline/poe/introspect.json"
SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
attestor: attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:1ff0a3124d66d3a2702d8e421df40fbd98cc75cb605d95510598ebbae1433c50 image: registry.stella-ops.org/stellaops/attestor@sha256:1ff0a3124d66d3a2702d8e421df40fbd98cc75cb605d95510598ebbae1433c50
service: service:
port: 8442 port: 8442
env: env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441" ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
concelier: concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:29e2e1a0972707e092cbd3d370701341f9fec2aa9316fb5d8100480f2a1c76b5 image: registry.stella-ops.org/stellaops/concelier@sha256:29e2e1a0972707e092cbd3d370701341f9fec2aa9316fb5d8100480f2a1c76b5
service: service:
port: 8445 port: 8445
env: env:
CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000" CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap" CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap"
CONCELIER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret" CONCELIER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true" CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "00:45:00" CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "00:45:00"
volumeMounts: volumeMounts:
- name: concelier-jobs - name: concelier-jobs
mountPath: /var/lib/concelier/jobs mountPath: /var/lib/concelier/jobs
volumeClaims: volumeClaims:
- name: concelier-jobs - name: concelier-jobs
claimName: stellaops-concelier-jobs claimName: stellaops-concelier-jobs
scanner-web: scanner-web:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:3df8ca21878126758203c1a0444e39fd97f77ddacf04a69685cda9f1e5e94718 image: registry.stella-ops.org/stellaops/scanner-web@sha256:3df8ca21878126758203c1a0444e39fd97f77ddacf04a69685cda9f1e5e94718
service: service:
port: 8444 port: 8444
env: env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
@@ -110,8 +110,12 @@ services:
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
SCANNER_SURFACE_SECRETS_PROVIDER: "file"
SCANNER_SURFACE_SECRETS_ROOT: "/etc/stellaops/secrets"
scanner-worker: scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5 image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5
env: env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
@@ -125,62 +129,66 @@ services:
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
notify-web: SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2 SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
service: SCANNER_SURFACE_SECRETS_PROVIDER: "file"
port: 8446 SCANNER_SURFACE_SECRETS_ROOT: "/etc/stellaops/secrets"
env: notify-web:
DOTNET_ENVIRONMENT: Production image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
configMounts: service:
- name: notify-config port: 8446
mountPath: /app/etc/notify.yaml env:
subPath: notify.yaml DOTNET_ENVIRONMENT: Production
configMap: notify-config configMounts:
excititor: - name: notify-config
image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68 mountPath: /app/etc/notify.yaml
env: subPath: notify.yaml
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445" configMap: notify-config
EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017" excititor:
web-ui: image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68
image: registry.stella-ops.org/stellaops/web-ui@sha256:bee9668011ff414572131dc777faab4da24473fe12c230893f161cabee092a1d env:
service: EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
port: 9443 EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
targetPort: 8443 web-ui:
env: image: registry.stella-ops.org/stellaops/web-ui@sha256:bee9668011ff414572131dc777faab4da24473fe12c230893f161cabee092a1d
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444" service:
mongo: port: 9443
class: infrastructure targetPort: 8443
image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49 env:
service: STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
port: 27017 mongo:
command: class: infrastructure
- mongod image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- --bind_ip_all service:
env: port: 27017
MONGO_INITDB_ROOT_USERNAME: stellaops-airgap command:
MONGO_INITDB_ROOT_PASSWORD: stellaops-airgap - mongod
volumeMounts: - --bind_ip_all
- name: mongo-data env:
mountPath: /data/db MONGO_INITDB_ROOT_USERNAME: stellaops-airgap
volumeClaims: MONGO_INITDB_ROOT_PASSWORD: stellaops-airgap
- name: mongo-data volumeMounts:
claimName: stellaops-mongo-data - name: mongo-data
mountPath: /data/db
volumeClaims:
- name: mongo-data
claimName: stellaops-mongo-data
minio: minio:
class: infrastructure class: infrastructure
image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
service: service:
port: 9000 port: 9000
command: command:
- server - server
- /data - /data
- --console-address - --console-address
- :9001 - :9001
env: env:
MINIO_ROOT_USER: stellaops-airgap MINIO_ROOT_USER: stellaops-airgap
MINIO_ROOT_PASSWORD: airgap-minio-secret MINIO_ROOT_PASSWORD: airgap-minio-secret
volumeMounts: volumeMounts:
- name: minio-data - name: minio-data
mountPath: /data mountPath: /data
volumeClaims: volumeClaims:
- name: minio-data - name: minio-data
claimName: stellaops-minio-data claimName: stellaops-minio-data
@@ -204,18 +212,18 @@ services:
volumeClaims: volumeClaims:
- name: rustfs-data - name: rustfs-data
claimName: stellaops-rustfs-data claimName: stellaops-rustfs-data
nats: nats:
class: infrastructure class: infrastructure
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
service: service:
port: 4222 port: 4222
command: command:
- -js - -js
- -sd - -sd
- /data - /data
volumeMounts: volumeMounts:
- name: nats-data - name: nats-data
mountPath: /data mountPath: /data
volumeClaims: volumeClaims:
- name: nats-data - name: nats-data
claimName: stellaops-nats-data claimName: stellaops-nats-data

View File

@@ -17,92 +17,92 @@ telemetry:
secretName: stellaops-otel-tls secretName: stellaops-otel-tls
configMaps: configMaps:
notify-config: notify-config:
data: data:
notify.yaml: | notify.yaml: |
storage: storage:
driver: mongo driver: mongo
connectionString: "mongodb://notify-mongo.dev.svc.cluster.local:27017" connectionString: "mongodb://notify-mongo.dev.svc.cluster.local:27017"
database: "stellaops_notify_dev" database: "stellaops_notify_dev"
commandTimeoutSeconds: 30 commandTimeoutSeconds: 30
authority: authority:
enabled: true enabled: true
issuer: "https://authority.dev.stella-ops.local" issuer: "https://authority.dev.stella-ops.local"
metadataAddress: "https://authority.dev.stella-ops.local/.well-known/openid-configuration" metadataAddress: "https://authority.dev.stella-ops.local/.well-known/openid-configuration"
requireHttpsMetadata: false requireHttpsMetadata: false
allowAnonymousFallback: false allowAnonymousFallback: false
backchannelTimeoutSeconds: 30 backchannelTimeoutSeconds: 30
tokenClockSkewSeconds: 60 tokenClockSkewSeconds: 60
audiences: audiences:
- notify.dev - notify.dev
readScope: notify.read readScope: notify.read
adminScope: notify.admin adminScope: notify.admin
api: api:
basePath: "/api/v1/notify" basePath: "/api/v1/notify"
internalBasePath: "/internal/notify" internalBasePath: "/internal/notify"
tenantHeader: "X-StellaOps-Tenant" tenantHeader: "X-StellaOps-Tenant"
plugins: plugins:
baseDirectory: "../" baseDirectory: "../"
directory: "plugins/notify" directory: "plugins/notify"
searchPatterns: searchPatterns:
- "StellaOps.Notify.Connectors.*.dll" - "StellaOps.Notify.Connectors.*.dll"
orderedPlugins: orderedPlugins:
- StellaOps.Notify.Connectors.Slack - StellaOps.Notify.Connectors.Slack
- StellaOps.Notify.Connectors.Teams - StellaOps.Notify.Connectors.Teams
- StellaOps.Notify.Connectors.Email - StellaOps.Notify.Connectors.Email
- StellaOps.Notify.Connectors.Webhook - StellaOps.Notify.Connectors.Webhook
telemetry: telemetry:
enableRequestLogging: true enableRequestLogging: true
minimumLogLevel: Debug minimumLogLevel: Debug
services: services:
authority: authority:
image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
service: service:
port: 8440 port: 8440
env: env:
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440" STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins" STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins" STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
signer: signer:
image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298 image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
service: service:
port: 8441 port: 8441
env: env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "https://licensing.svc.local/introspect" SIGNER__POE__INTROSPECTURL: "https://licensing.svc.local/introspect"
SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
attestor: attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114 image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
service: service:
port: 8442 port: 8442
env: env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441" ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
concelier: concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085 image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
service: service:
port: 8445 port: 8445
env: env:
CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000" CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops" CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops"
CONCELIER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret" CONCELIER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
volumeMounts: volumeMounts:
- name: concelier-jobs - name: concelier-jobs
mountPath: /var/lib/concelier/jobs mountPath: /var/lib/concelier/jobs
volumes: volumes:
- name: concelier-jobs - name: concelier-jobs
emptyDir: {} emptyDir: {}
scanner-web: scanner-web:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:e0dfdb087e330585a5953029fb4757f5abdf7610820a085bd61b457dbead9a11 image: registry.stella-ops.org/stellaops/scanner-web@sha256:e0dfdb087e330585a5953029fb4757f5abdf7610820a085bd61b457dbead9a11
service: service:
port: 8444 port: 8444
env: env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
@@ -116,8 +116,12 @@ services:
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
SCANNER_SURFACE_SECRETS_PROVIDER: "inline"
SCANNER_SURFACE_SECRETS_ROOT: ""
scanner-worker: scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37 image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
env: env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
@@ -131,61 +135,65 @@ services:
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
notify-web: SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
image: registry.stella-ops.org/stellaops/notify-web:2025.10.0-edge SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
service: SCANNER_SURFACE_SECRETS_PROVIDER: "inline"
port: 8446 SCANNER_SURFACE_SECRETS_ROOT: ""
env: notify-web:
DOTNET_ENVIRONMENT: Development image: registry.stella-ops.org/stellaops/notify-web:2025.10.0-edge
configMounts: service:
- name: notify-config port: 8446
mountPath: /app/etc/notify.yaml env:
subPath: notify.yaml DOTNET_ENVIRONMENT: Development
configMap: notify-config configMounts:
excititor: - name: notify-config
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285 mountPath: /app/etc/notify.yaml
env: subPath: notify.yaml
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445" configMap: notify-config
EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017" excititor:
web-ui: image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
image: registry.stella-ops.org/stellaops/web-ui@sha256:38b225fa7767a5b94ebae4dae8696044126aac429415e93de514d5dd95748dcf env:
service: EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
port: 8443 EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
env: web-ui:
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444" image: registry.stella-ops.org/stellaops/web-ui@sha256:38b225fa7767a5b94ebae4dae8696044126aac429415e93de514d5dd95748dcf
mongo: service:
class: infrastructure port: 8443
image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49 env:
service: STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
port: 27017 mongo:
command: class: infrastructure
- mongod image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- --bind_ip_all service:
env: port: 27017
MONGO_INITDB_ROOT_USERNAME: stellaops command:
MONGO_INITDB_ROOT_PASSWORD: stellaops - mongod
volumeMounts: - --bind_ip_all
- name: mongo-data env:
mountPath: /data/db MONGO_INITDB_ROOT_USERNAME: stellaops
volumes: MONGO_INITDB_ROOT_PASSWORD: stellaops
- name: mongo-data volumeMounts:
emptyDir: {} - name: mongo-data
mountPath: /data/db
volumes:
- name: mongo-data
emptyDir: {}
minio: minio:
class: infrastructure class: infrastructure
image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
service: service:
port: 9000 port: 9000
command: command:
- server - server
- /data - /data
- --console-address - --console-address
- :9001 - :9001
env: env:
MINIO_ROOT_USER: stellaops MINIO_ROOT_USER: stellaops
MINIO_ROOT_PASSWORD: dev-minio-secret MINIO_ROOT_PASSWORD: dev-minio-secret
volumeMounts: volumeMounts:
- name: minio-data - name: minio-data
mountPath: /data mountPath: /data
volumes: volumes:
- name: minio-data - name: minio-data
emptyDir: {} emptyDir: {}
@@ -203,18 +211,18 @@ services:
volumes: volumes:
- name: rustfs-data - name: rustfs-data
emptyDir: {} emptyDir: {}
nats: nats:
class: infrastructure class: infrastructure
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
service: service:
port: 4222 port: 4222
command: command:
- -js - -js
- -sd - -sd
- /data - /data
volumeMounts: volumeMounts:
- name: nats-data - name: nats-data
mountPath: /data mountPath: /data
volumes: volumes:
- name: nats-data - name: nats-data
emptyDir: {} emptyDir: {}

View File

@@ -1,221 +1,229 @@
global: global:
profile: prod profile: prod
release: release:
version: "2025.09.2" version: "2025.09.2"
channel: stable channel: stable
manifestSha256: "dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7" manifestSha256: "dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7"
image: image:
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
labels: labels:
stellaops.io/channel: stable stellaops.io/channel: stable
stellaops.io/profile: prod stellaops.io/profile: prod
configMaps: configMaps:
notify-config: notify-config:
data: data:
notify.yaml: | notify.yaml: |
storage: storage:
driver: mongo driver: mongo
connectionString: "mongodb://stellaops-mongo:27017" connectionString: "mongodb://stellaops-mongo:27017"
database: "stellaops_notify_prod" database: "stellaops_notify_prod"
commandTimeoutSeconds: 45 commandTimeoutSeconds: 45
authority: authority:
enabled: true enabled: true
issuer: "https://authority.prod.stella-ops.org" issuer: "https://authority.prod.stella-ops.org"
metadataAddress: "https://authority.prod.stella-ops.org/.well-known/openid-configuration" metadataAddress: "https://authority.prod.stella-ops.org/.well-known/openid-configuration"
requireHttpsMetadata: true requireHttpsMetadata: true
allowAnonymousFallback: false allowAnonymousFallback: false
backchannelTimeoutSeconds: 30 backchannelTimeoutSeconds: 30
tokenClockSkewSeconds: 60 tokenClockSkewSeconds: 60
audiences: audiences:
- notify - notify
readScope: notify.read readScope: notify.read
adminScope: notify.admin adminScope: notify.admin
api: api:
basePath: "/api/v1/notify" basePath: "/api/v1/notify"
internalBasePath: "/internal/notify" internalBasePath: "/internal/notify"
tenantHeader: "X-StellaOps-Tenant" tenantHeader: "X-StellaOps-Tenant"
plugins: plugins:
baseDirectory: "/opt/stellaops" baseDirectory: "/opt/stellaops"
directory: "plugins/notify" directory: "plugins/notify"
searchPatterns: searchPatterns:
- "StellaOps.Notify.Connectors.*.dll" - "StellaOps.Notify.Connectors.*.dll"
orderedPlugins: orderedPlugins:
- StellaOps.Notify.Connectors.Slack - StellaOps.Notify.Connectors.Slack
- StellaOps.Notify.Connectors.Teams - StellaOps.Notify.Connectors.Teams
- StellaOps.Notify.Connectors.Email - StellaOps.Notify.Connectors.Email
- StellaOps.Notify.Connectors.Webhook - StellaOps.Notify.Connectors.Webhook
telemetry: telemetry:
enableRequestLogging: true enableRequestLogging: true
minimumLogLevel: Information minimumLogLevel: Information
services: services:
authority: authority:
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5 image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
service: service:
port: 8440 port: 8440
env: env:
STELLAOPS_AUTHORITY__ISSUER: "https://authority.prod.stella-ops.org" STELLAOPS_AUTHORITY__ISSUER: "https://authority.prod.stella-ops.org"
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins" STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins" STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
envFrom: envFrom:
- secretRef: - secretRef:
name: stellaops-prod-core name: stellaops-prod-core
signer: signer:
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
service: service:
port: 8441 port: 8441
env: env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "https://licensing.prod.stella-ops.org/introspect" SIGNER__POE__INTROSPECTURL: "https://licensing.prod.stella-ops.org/introspect"
envFrom: envFrom:
- secretRef: - secretRef:
name: stellaops-prod-core name: stellaops-prod-core
attestor: attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
service: service:
port: 8442 port: 8442
env: env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441" ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
envFrom: envFrom:
- secretRef: - secretRef:
name: stellaops-prod-core name: stellaops-prod-core
concelier: concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5 image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
service: service:
port: 8445 port: 8445
env: env:
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000" CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
envFrom: envFrom:
- secretRef: - secretRef:
name: stellaops-prod-core name: stellaops-prod-core
volumeMounts: volumeMounts:
- name: concelier-jobs - name: concelier-jobs
mountPath: /var/lib/concelier/jobs mountPath: /var/lib/concelier/jobs
volumeClaims: volumeClaims:
- name: concelier-jobs - name: concelier-jobs
claimName: stellaops-concelier-jobs claimName: stellaops-concelier-jobs
scanner-web: scanner-web:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7 image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
service: service:
port: 8444 port: 8444
env: env:
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1" SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts" SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30" SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222" SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__ENABLED: "true" SCANNER__EVENTS__ENABLED: "true"
SCANNER__EVENTS__DRIVER: "redis" SCANNER__EVENTS__DRIVER: "redis"
SCANNER__EVENTS__DSN: "" SCANNER__EVENTS__DSN: ""
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
envFrom: SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
- secretRef: SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
name: stellaops-prod-core SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
scanner-worker: SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab envFrom:
replicas: 3 - secretRef:
env: name: stellaops-prod-core
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" scanner-worker:
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1" image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts" replicas: 3
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30" env:
SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__EVENTS__ENABLED: "true" SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__EVENTS__DRIVER: "redis" SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__EVENTS__DSN: "" SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__ENABLED: "true"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__DRIVER: "redis"
envFrom: SCANNER__EVENTS__DSN: ""
- secretRef: SCANNER__EVENTS__STREAM: "stella.events"
name: stellaops-prod-core SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
notify-web: SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2 SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
service: SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
port: 8446 SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
env: SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
DOTNET_ENVIRONMENT: Production envFrom:
envFrom: - secretRef:
- secretRef: name: stellaops-prod-core
name: stellaops-prod-notify notify-web:
configMounts: image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
- name: notify-config service:
mountPath: /app/etc/notify.yaml port: 8446
subPath: notify.yaml env:
configMap: notify-config DOTNET_ENVIRONMENT: Production
excititor: envFrom:
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa - secretRef:
env: name: stellaops-prod-notify
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445" configMounts:
envFrom: - name: notify-config
- secretRef: mountPath: /app/etc/notify.yaml
name: stellaops-prod-core subPath: notify.yaml
web-ui: configMap: notify-config
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23 excititor:
service: image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
port: 8443 env:
env: EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444" envFrom:
mongo: - secretRef:
class: infrastructure name: stellaops-prod-core
image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49 web-ui:
service: image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
port: 27017 service:
command: port: 8443
- mongod env:
- --bind_ip_all STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
envFrom: mongo:
- secretRef: class: infrastructure
name: stellaops-prod-mongo image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
volumeMounts: service:
- name: mongo-data port: 27017
mountPath: /data/db command:
volumeClaims: - mongod
- name: mongo-data - --bind_ip_all
claimName: stellaops-mongo-data envFrom:
minio: - secretRef:
class: infrastructure name: stellaops-prod-mongo
image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e volumeMounts:
service: - name: mongo-data
port: 9000 mountPath: /data/db
command: volumeClaims:
- server - name: mongo-data
- /data claimName: stellaops-mongo-data
- --console-address minio:
- :9001 class: infrastructure
envFrom: image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
- secretRef: service:
name: stellaops-prod-minio port: 9000
volumeMounts: command:
- name: minio-data - server
mountPath: /data - /data
volumeClaims: - --console-address
- name: minio-data - :9001
claimName: stellaops-minio-data envFrom:
rustfs: - secretRef:
class: infrastructure name: stellaops-prod-minio
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge volumeMounts:
service: - name: minio-data
port: 8080 mountPath: /data
command: volumeClaims:
- serve - name: minio-data
- --listen claimName: stellaops-minio-data
- 0.0.0.0:8080 rustfs:
- --root class: infrastructure
- /data image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
env: service:
RUSTFS__LOG__LEVEL: info port: 8080
RUSTFS__STORAGE__PATH: /data command:
volumeMounts: - serve
- name: rustfs-data - --listen
mountPath: /data - 0.0.0.0:8080
volumeClaims: - --root
- name: rustfs-data - /data
claimName: stellaops-rustfs-data env:
RUSTFS__LOG__LEVEL: info
RUSTFS__STORAGE__PATH: /data
volumeMounts:
- name: rustfs-data
mountPath: /data
volumeClaims:
- name: rustfs-data
claimName: stellaops-rustfs-data

View File

@@ -2,10 +2,10 @@ global:
profile: stage profile: stage
release: release:
version: "2025.09.2" version: "2025.09.2"
channel: stable channel: stable
manifestSha256: "dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7" manifestSha256: "dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7"
image: image:
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
labels: labels:
stellaops.io/channel: stable stellaops.io/channel: stable
@@ -15,94 +15,94 @@ telemetry:
defaultTenant: stage defaultTenant: stage
tls: tls:
secretName: stellaops-otel-tls-stage secretName: stellaops-otel-tls-stage
configMaps: configMaps:
notify-config: notify-config:
data: data:
notify.yaml: | notify.yaml: |
storage: storage:
driver: mongo driver: mongo
connectionString: "mongodb://notify-mongo.stage.svc.cluster.local:27017" connectionString: "mongodb://notify-mongo.stage.svc.cluster.local:27017"
database: "stellaops_notify_stage" database: "stellaops_notify_stage"
commandTimeoutSeconds: 45 commandTimeoutSeconds: 45
authority: authority:
enabled: true enabled: true
issuer: "https://authority.stage.stella-ops.org" issuer: "https://authority.stage.stella-ops.org"
metadataAddress: "https://authority.stage.stella-ops.org/.well-known/openid-configuration" metadataAddress: "https://authority.stage.stella-ops.org/.well-known/openid-configuration"
requireHttpsMetadata: true requireHttpsMetadata: true
allowAnonymousFallback: false allowAnonymousFallback: false
backchannelTimeoutSeconds: 30 backchannelTimeoutSeconds: 30
tokenClockSkewSeconds: 60 tokenClockSkewSeconds: 60
audiences: audiences:
- notify - notify
readScope: notify.read readScope: notify.read
adminScope: notify.admin adminScope: notify.admin
api: api:
basePath: "/api/v1/notify" basePath: "/api/v1/notify"
internalBasePath: "/internal/notify" internalBasePath: "/internal/notify"
tenantHeader: "X-StellaOps-Tenant" tenantHeader: "X-StellaOps-Tenant"
plugins: plugins:
baseDirectory: "/opt/stellaops" baseDirectory: "/opt/stellaops"
directory: "plugins/notify" directory: "plugins/notify"
searchPatterns: searchPatterns:
- "StellaOps.Notify.Connectors.*.dll" - "StellaOps.Notify.Connectors.*.dll"
orderedPlugins: orderedPlugins:
- StellaOps.Notify.Connectors.Slack - StellaOps.Notify.Connectors.Slack
- StellaOps.Notify.Connectors.Teams - StellaOps.Notify.Connectors.Teams
- StellaOps.Notify.Connectors.Email - StellaOps.Notify.Connectors.Email
- StellaOps.Notify.Connectors.Webhook - StellaOps.Notify.Connectors.Webhook
telemetry: telemetry:
enableRequestLogging: true enableRequestLogging: true
minimumLogLevel: Information minimumLogLevel: Information
services: services:
authority: authority:
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5 image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
service: service:
port: 8440 port: 8440
env: env:
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440" STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins" STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins" STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
signer: signer:
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
service: service:
port: 8441 port: 8441
env: env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "https://licensing.stage.stella-ops.internal/introspect" SIGNER__POE__INTROSPECTURL: "https://licensing.stage.stella-ops.internal/introspect"
SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
attestor: attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
service: service:
port: 8442 port: 8442
env: env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441" ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
concelier: concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5 image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
service: service:
port: 8445 port: 8445
env: env:
CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000" CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-stage" CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-stage"
CONCELIER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret" CONCELIER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440" CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
volumeMounts: volumeMounts:
- name: concelier-jobs - name: concelier-jobs
mountPath: /var/lib/concelier/jobs mountPath: /var/lib/concelier/jobs
volumeClaims: volumeClaims:
- name: concelier-jobs - name: concelier-jobs
claimName: stellaops-concelier-jobs claimName: stellaops-concelier-jobs
scanner-web: scanner-web:
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7 image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
service: service:
port: 8444 port: 8444
env: env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
@@ -116,9 +116,13 @@ services:
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
scanner-worker: scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
replicas: 2 replicas: 2
env: env:
SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs" SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
@@ -132,61 +136,65 @@ services:
SCANNER__EVENTS__STREAM: "stella.events" SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5" SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000" SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
notify-web: SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2 SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
service: SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
port: 8446 SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
env: notify-web:
DOTNET_ENVIRONMENT: Production image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
configMounts: service:
- name: notify-config port: 8446
mountPath: /app/etc/notify.yaml env:
subPath: notify.yaml DOTNET_ENVIRONMENT: Production
configMap: notify-config configMounts:
excititor: - name: notify-config
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa mountPath: /app/etc/notify.yaml
env: subPath: notify.yaml
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445" configMap: notify-config
EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017" excititor:
web-ui: image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23 env:
service: EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
port: 8443 EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
env: web-ui:
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444" image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
mongo: service:
class: infrastructure port: 8443
image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49 env:
service: STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
port: 27017 mongo:
command: class: infrastructure
- mongod image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- --bind_ip_all service:
env: port: 27017
MONGO_INITDB_ROOT_USERNAME: stellaops-stage command:
MONGO_INITDB_ROOT_PASSWORD: stellaops-stage - mongod
volumeMounts: - --bind_ip_all
- name: mongo-data env:
mountPath: /data/db MONGO_INITDB_ROOT_USERNAME: stellaops-stage
volumeClaims: MONGO_INITDB_ROOT_PASSWORD: stellaops-stage
- name: mongo-data volumeMounts:
claimName: stellaops-mongo-data - name: mongo-data
mountPath: /data/db
volumeClaims:
- name: mongo-data
claimName: stellaops-mongo-data
minio: minio:
class: infrastructure class: infrastructure
image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
service: service:
port: 9000 port: 9000
command: command:
- server - server
- /data - /data
- --console-address - --console-address
- :9001 - :9001
env: env:
MINIO_ROOT_USER: stellaops-stage MINIO_ROOT_USER: stellaops-stage
MINIO_ROOT_PASSWORD: stage-minio-secret MINIO_ROOT_PASSWORD: stage-minio-secret
volumeMounts: volumeMounts:
- name: minio-data - name: minio-data
mountPath: /data mountPath: /data
volumeClaims: volumeClaims:
- name: minio-data - name: minio-data
claimName: stellaops-minio-data claimName: stellaops-minio-data
@@ -210,18 +218,18 @@ services:
volumeClaims: volumeClaims:
- name: rustfs-data - name: rustfs-data
claimName: stellaops-rustfs-data claimName: stellaops-rustfs-data
nats: nats:
class: infrastructure class: infrastructure
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
service: service:
port: 4222 port: 4222
command: command:
- -js - -js
- -sd - -sd
- /data - /data
volumeMounts: volumeMounts:
- name: nats-data - name: nats-data
mountPath: /data mountPath: /data
volumeClaims: volumeClaims:
- name: nats-data - name: nats-data
claimName: stellaops-nats-data claimName: stellaops-nats-data

View File

@@ -227,7 +227,7 @@ EXCITITOR-AIRGAP-56-002 `Bundle provenance` | TODO | Persist bundle metadata on
EXCITITOR-AIRGAP-57-001 `Sealed-mode enforcement` | TODO | Block non-mirror connectors in sealed mode and surface remediation errors. Dependencies: EXCITITOR-AIRGAP-56-002. | Excititor Core Guild, AirGap Policy Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md) EXCITITOR-AIRGAP-57-001 `Sealed-mode enforcement` | TODO | Block non-mirror connectors in sealed mode and surface remediation errors. Dependencies: EXCITITOR-AIRGAP-56-002. | Excititor Core Guild, AirGap Policy Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md)
EXCITITOR-AIRGAP-57-002 `Staleness annotations` | TODO | Annotate VEX statements with staleness metrics and expose via API. Dependencies: EXCITITOR-AIRGAP-57-001. | Excititor Core Guild, AirGap Time Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md) EXCITITOR-AIRGAP-57-002 `Staleness annotations` | TODO | Annotate VEX statements with staleness metrics and expose via API. Dependencies: EXCITITOR-AIRGAP-57-001. | Excititor Core Guild, AirGap Time Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md)
EXCITITOR-AIRGAP-58-001 `Portable VEX evidence` | TODO | Package VEX evidence segments into portable evidence bundles linked to timeline. Dependencies: EXCITITOR-AIRGAP-57-002. | Excititor Core Guild, Evidence Locker Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md) EXCITITOR-AIRGAP-58-001 `Portable VEX evidence` | TODO | Package VEX evidence segments into portable evidence bundles linked to timeline. Dependencies: EXCITITOR-AIRGAP-57-002. | Excititor Core Guild, Evidence Locker Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md)
EXCITITOR-ATTEST-01-003 Verification suite & observability | Team Excititor Attestation | DOING (2025-10-22) Continuing implementation: build `IVexAttestationVerifier`, wire metrics/logging, and add regression tests. Draft plan in `EXCITITOR-ATTEST-01-003-plan.md` (2025-10-19) guides scope; updating with worknotes as progress lands.<br>2025-10-31: Verifier now tolerates duplicate source providers from AOC raw projections, downgrades offline Rekor verification to a degraded result, and enforces trusted signer registry checks with detailed diagnostics/tests.<br>2025-11-05 14:35Z: Resuming with diagnostics/observability deliverables (typed diagnostics record, ActivitySource wiring, metrics dimensions) before WebService/Worker integration. | EXCITITOR-ATTEST-01-002 (src/Excititor/__Libraries/StellaOps.Excititor.Attestation/TASKS.md) EXCITITOR-ATTEST-01-003 Verification suite & observability | Team Excititor Attestation | TODO (2025-11-06) Continuing implementation: build `IVexAttestationVerifier`, wire metrics/logging, and add regression tests. Draft plan in `EXCITITOR-ATTEST-01-003-plan.md` (2025-10-19) guides scope; updating with worknotes as progress lands.<br>2025-10-31: Verifier now tolerates duplicate source providers from AOC raw projections, downgrades offline Rekor verification to a degraded result, and enforces trusted signer registry checks with detailed diagnostics/tests.<br>2025-11-05 14:35Z: Resuming with diagnostics/observability deliverables (typed diagnostics record, ActivitySource wiring, metrics dimensions) before WebService/Worker integration.<br>2025-11-06 07:12Z: Worker & web service suites pass with new diagnostics (`dotnet test` via staged libssl1.1); export envelope context exposed publicly for mirror bundle publishing.<br>2025-11-06 07:55Z: Paused—automation for OpenSSL shim tracked under `DEVOPS-OPENSSL-11-001/002`. | EXCITITOR-ATTEST-01-002 (src/Excititor/__Libraries/StellaOps.Excititor.Attestation/TASKS.md)
EXCITITOR-ATTEST-73-001 `VEX attestation payloads` | TODO | Provide VEX statement metadata (supplier identity, justification, scope) required for VEXAttestation payloads. Dependencies: EXCITITOR-ATTEST-01-003. | Excititor Core Guild, Attestation Payloads Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md) EXCITITOR-ATTEST-73-001 `VEX attestation payloads` | TODO | Provide VEX statement metadata (supplier identity, justification, scope) required for VEXAttestation payloads. Dependencies: EXCITITOR-ATTEST-01-003. | Excititor Core Guild, Attestation Payloads Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md)
EXCITITOR-ATTEST-73-002 `Chain provenance` | TODO | Expose linkage from VEX statements to subject/product for chain of custody graph. Dependencies: EXCITITOR-ATTEST-73-001. | Excititor Core Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md) EXCITITOR-ATTEST-73-002 `Chain provenance` | TODO | Expose linkage from VEX statements to subject/product for chain of custody graph. Dependencies: EXCITITOR-ATTEST-73-001. | Excititor Core Guild (src/Excititor/__Libraries/StellaOps.Excititor.Core/TASKS.md)
EXCITITOR-CONN-MS-01-003 Trust metadata & provenance hints | Team Excititor Connectors MSRC | TODO Emit cosign/AAD issuer metadata, attach provenance details, and document policy integration. | EXCITITOR-CONN-MS-01-002, EXCITITOR-POLICY-01-001 (src/Excititor/__Libraries/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md) EXCITITOR-CONN-MS-01-003 Trust metadata & provenance hints | Team Excititor Connectors MSRC | TODO Emit cosign/AAD issuer metadata, attach provenance details, and document policy integration. | EXCITITOR-CONN-MS-01-002, EXCITITOR-POLICY-01-001 (src/Excititor/__Libraries/StellaOps.Excititor.Connectors.MSRC.CSAF/TASKS.md)

View File

@@ -134,8 +134,8 @@ Summary: Scanner & Surface focus on Scanner (phase VII).
Task ID | State | Task description | Owners (Source) Task ID | State | Task description | Owners (Source)
--- | --- | --- | --- --- | --- | --- | ---
SCANNER-ENTRYTRACE-18-504 | TODO | Emit EntryTrace AOC NDJSON (`entrytrace.entry/node/edge/target/warning/capability`) and wire CLI/service streaming outputs. Dependencies: SCANNER-ENTRYTRACE-18-503. | EntryTrace Guild (src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md) SCANNER-ENTRYTRACE-18-504 | TODO | Emit EntryTrace AOC NDJSON (`entrytrace.entry/node/edge/target/warning/capability`) and wire CLI/service streaming outputs. Dependencies: SCANNER-ENTRYTRACE-18-503. | EntryTrace Guild (src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md)
SCANNER-ENV-01 | DOING (2025-11-02) | Replace ad-hoc environment reads with `StellaOps.Scanner.Surface.Env` helpers for cache roots and CAS endpoints.<br>2025-11-02: Env helper wiring drafted for Worker startup; initial tests validate cache root resolution.<br>2025-11-05 14:55Z: Continuing integration by propagating resolved settings into cache/secret services and prepping worker smoke tests + docs updates.<br>2025-11-05 19:18Z: Bound `SurfaceCacheOptions` root to Surface.Env settings and added configurator unit coverage.<br>2025-11-06 17:05Z: Documented misconfiguration warnings and updated module README to highlight Surface.Env usage. | Scanner Worker Guild (src/Scanner/StellaOps.Scanner.Worker/TASKS.md) SCANNER-ENV-01 | TODO (2025-11-06) | Replace ad-hoc environment reads with `StellaOps.Scanner.Surface.Env` helpers for cache roots and CAS endpoints.<br>2025-11-02: Env helper wiring drafted for Worker startup; initial tests validate cache root resolution.<br>2025-11-05 14:55Z: Continuing integration by propagating resolved settings into cache/secret services and prepping worker smoke tests + docs updates.<br>2025-11-05 19:18Z: Bound `SurfaceCacheOptions` root to Surface.Env settings and added configurator unit coverage.<br>2025-11-06 17:05Z: Documented misconfiguration warnings and updated module README to highlight Surface.Env usage.<br>2025-11-06 07:45Z: Helm/Compose env profiles (dev/stage/prod/airgap/mirror) now emit `SCANNER_SURFACE_*` defaults and ops README covers rollout warnings.<br>2025-11-06 07:55Z: Paused pending automation tracked under `DEVOPS-OPENSSL-11-001/002` and additional Surface.Env fixtures. | Scanner Worker Guild (src/Scanner/StellaOps.Scanner.Worker/TASKS.md)
SCANNER-ENV-02 | DOING (2025-11-02) | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration. Dependencies: SCANNER-ENV-01.<br>2025-11-02: WebService bootstrap now consumes Surface.Env helpers for cache roots and feature flag toggles; configuration doc draft pending.<br>2025-11-05 14:55Z: Picking up configuration/documentation work and aligning API readiness checks with Surface.Env validation outputs.<br>2025-11-05 19:18Z: Added unit test for Surface.Env cache root binding and ensured configurator registration.<br>2025-11-06 17:05Z: Surface.Env design doc expanded with warning catalogue and release notes, README refreshed. | Scanner WebService Guild, Ops Guild (src/Scanner/StellaOps.Scanner.WebService/TASKS.md) SCANNER-ENV-02 | TODO (2025-11-06) | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration. Dependencies: SCANNER-ENV-01.<br>2025-11-02: WebService bootstrap now consumes Surface.Env helpers for cache roots and feature flag toggles; configuration doc draft pending.<br>2025-11-05 14:55Z: Picking up configuration/documentation work and aligning API readiness checks with Surface.Env validation outputs.<br>2025-11-05 19:18Z: Added unit test for Surface.Env cache root binding and ensured configurator registration.<br>2025-11-06 17:05Z: Surface.Env design doc expanded with warning catalogue and release notes, README refreshed.<br>2025-11-06 07:45Z: Helm/Compose templates ship `SCANNER_SURFACE_*` defaults across dev/stage/prod/airgap/mirror profiles with rollout guidance in deploy docs.<br>2025-11-06 07:55Z: Paused; follow-up automation tracked under `DEVOPS-OPENSSL-11-001/002` and readiness tests outstanding. | Scanner WebService Guild, Ops Guild (src/Scanner/StellaOps.Scanner.WebService/TASKS.md)
SCANNER-ENV-03 | TODO | Adopt Surface.Env helpers for plugin configuration (cache roots, CAS endpoints, feature toggles). Dependencies: SCANNER-ENV-02. | BuildX Plugin Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md) SCANNER-ENV-03 | TODO | Adopt Surface.Env helpers for plugin configuration (cache roots, CAS endpoints, feature toggles). Dependencies: SCANNER-ENV-02. | BuildX Plugin Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin/TASKS.md)
SCANNER-EVENTS-16-301 | BLOCKED (2025-10-26) | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). | Scanner WebService Guild (src/Scanner/StellaOps.Scanner.WebService/TASKS.md) SCANNER-EVENTS-16-301 | BLOCKED (2025-10-26) | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). | Scanner WebService Guild (src/Scanner/StellaOps.Scanner.WebService/TASKS.md)
SCANNER-EVENTS-16-302 | DOING (2025-10-26) | Extend orchestrator event links (report/policy/attestation) once endpoints are finalised across gateway + console. Dependencies: SCANNER-EVENTS-16-301. | Scanner WebService Guild (src/Scanner/StellaOps.Scanner.WebService/TASKS.md) SCANNER-EVENTS-16-302 | DOING (2025-10-26) | Extend orchestrator event links (report/policy/attestation) once endpoints are finalised across gateway + console. Dependencies: SCANNER-EVENTS-16-301. | Scanner WebService Guild (src/Scanner/StellaOps.Scanner.WebService/TASKS.md)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -13,6 +13,9 @@
> Docs hand-off (2025-10-26): see `docs/ingestion/aggregation-only-contract.md` §5, `docs/modules/platform/architecture-overview.md`, and `docs/modules/cli/guides/cli-reference.md` for guard + verifier expectations. > Docs hand-off (2025-10-26): see `docs/ingestion/aggregation-only-contract.md` §5, `docs/modules/platform/architecture-overview.md`, and `docs/modules/cli/guides/cli-reference.md` for guard + verifier expectations.
| DEVOPS-AOC-19-002 | BLOCKED (2025-10-26) | DevOps Guild | CLI-AOC-19-002, CONCELIER-WEB-AOC-19-004, EXCITITOR-WEB-AOC-19-004 | Add pipeline stage executing `stella aoc verify --since` against seeded Mongo snapshots for Concelier + Excititor, publishing violation report artefacts. | Stage runs on main/nightly, fails on violations, artifacts retained, runbook documented. | | DEVOPS-AOC-19-002 | BLOCKED (2025-10-26) | DevOps Guild | CLI-AOC-19-002, CONCELIER-WEB-AOC-19-004, EXCITITOR-WEB-AOC-19-004 | Add pipeline stage executing `stella aoc verify --since` against seeded Mongo snapshots for Concelier + Excititor, publishing violation report artefacts. | Stage runs on main/nightly, fails on violations, artifacts retained, runbook documented. |
> Blocked: waiting on CLI verifier command and Concelier/Excititor guard endpoints to land (CLI-AOC-19-002, CONCELIER-WEB-AOC-19-004, EXCITITOR-WEB-AOC-19-004). > Blocked: waiting on CLI verifier command and Concelier/Excititor guard endpoints to land (CLI-AOC-19-002, CONCELIER-WEB-AOC-19-004, EXCITITOR-WEB-AOC-19-004).
| DEVOPS-OPENSSL-11-001 | TODO (2025-11-06) | DevOps Guild, Build Infra Guild | — | Package the OpenSSL 1.1 shim (`tests/native/openssl-1.1/linux-x64`) into test harness output so Mongo2Go suites discover it automatically. | Shim copied during `dotnet test`, documentation updated, redundant manual extraction removed. |
> 2025-11-06: Interim guidance published in `tests/native/openssl-1.1/README.md` and `deploy/README.md`; automation still required.
| DEVOPS-OPENSSL-11-002 | TODO (2025-11-06) | DevOps Guild, CI Guild | DEVOPS-OPENSSL-11-001 | Ensure CI runners and Docker images that execute Mongo2Go tests export `LD_LIBRARY_PATH` (or embed the shim) to unblock unattended pipelines. | CI jobs set the variable or bake the libraries; runbook documents the location; smoke builds green without manual exports. |
| DEVOPS-AOC-19-003 | BLOCKED (2025-10-26) | DevOps Guild, QA Guild | CONCELIER-WEB-AOC-19-003, EXCITITOR-WEB-AOC-19-003 | Enforce unit test coverage thresholds for AOC guard suites and ensure coverage exported to dashboards. | Coverage report includes guard projects, threshold gate passes/fails as expected, dashboards refreshed with new metrics. | | DEVOPS-AOC-19-003 | BLOCKED (2025-10-26) | DevOps Guild, QA Guild | CONCELIER-WEB-AOC-19-003, EXCITITOR-WEB-AOC-19-003 | Enforce unit test coverage thresholds for AOC guard suites and ensure coverage exported to dashboards. | Coverage report includes guard projects, threshold gate passes/fails as expected, dashboards refreshed with new metrics. |
> Blocked: guard coverage suites and exporter hooks pending in Concelier/Excititor (CONCELIER-WEB-AOC-19-003, EXCITITOR-WEB-AOC-19-003). > Blocked: guard coverage suites and exporter hooks pending in Concelier/Excititor (CONCELIER-WEB-AOC-19-003, EXCITITOR-WEB-AOC-19-003).
| DEVOPS-AOC-19-101 | TODO (2025-10-28) | DevOps Guild, Concelier Storage Guild | CONCELIER-STORE-AOC-19-002 | Draft supersedes backfill rollout (freeze window, dry-run steps, rollback) once advisory_raw idempotency index passes staging verification. | Runbook committed in `docs/deploy/containers.md` + Offline Kit notes, staging rehearsal scheduled with dependencies captured in SPRINTS. | | DEVOPS-AOC-19-101 | TODO (2025-10-28) | DevOps Guild, Concelier Storage Guild | CONCELIER-STORE-AOC-19-002 | Draft supersedes backfill rollout (freeze window, dry-run steps, rollback) once advisory_raw idempotency index passes staging verification. | Runbook committed in `docs/deploy/containers.md` + Offline Kit notes, staging rehearsal scheduled with dependencies captured in SPRINTS. |

View File

@@ -18,7 +18,6 @@ using StellaOps.Excititor.Policy;
using StellaOps.Excititor.Storage.Mongo; using StellaOps.Excititor.Storage.Mongo;
using StellaOps.Excititor.WebService.Endpoints; using StellaOps.Excititor.WebService.Endpoints;
using StellaOps.Excititor.WebService.Services; using StellaOps.Excititor.WebService.Services;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Core.Aoc; using StellaOps.Excititor.Core.Aoc;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);

View File

@@ -2,6 +2,8 @@ If you are working on this file you need to read docs/modules/excititor/ARCHITEC
# TASKS # TASKS
| Task | Owner(s) | Depends on | Notes | | Task | Owner(s) | Depends on | Notes |
|---|---|---|---| |---|---|---|---|
|EXCITITOR-ATTEST-01-003 Verification suite & observability|Team Excititor Attestation|EXCITITOR-ATTEST-01-002|DOING (2025-10-22) Continuing implementation: build `IVexAttestationVerifier`, wire metrics/logging, and add regression tests. Draft plan in `EXCITITOR-ATTEST-01-003-plan.md` (2025-10-19) guides scope; updating with worknotes as progress lands.<br>2025-10-31: Verifier now tolerates duplicate source providers from AOC raw projections, downgrades offline Rekor verification to a degraded result, and enforces trusted signer registry checks with detailed diagnostics/tests.<br>2025-11-05 14:35Z: Picking up diagnostics record/ActivitySource work and aligning metrics dimensions before wiring verifier into WebService/Worker paths.| |EXCITITOR-ATTEST-01-003 Verification suite & observability|Team Excititor Attestation|EXCITITOR-ATTEST-01-002|TODO (2025-11-06) Continuing implementation: build `IVexAttestationVerifier`, wire metrics/logging, and add regression tests. Draft plan in `EXCITITOR-ATTEST-01-003-plan.md` (2025-10-19) guides scope; updating with worknotes as progress lands.<br>2025-10-31: Verifier now tolerates duplicate source providers from AOC raw projections, downgrades offline Rekor verification to a degraded result, and enforces trusted signer registry checks with detailed diagnostics/tests.<br>2025-11-05 14:35Z: Picking up diagnostics record/ActivitySource work and aligning metrics dimensions before wiring verifier into WebService/Worker paths.|
> 2025-11-05 19:10Z: Worker signature verifier now emits structured diagnostics/metrics via `VexAttestationDiagnostics`; attestation verification results flow into metric labels and logs. > 2025-11-05 19:10Z: Worker signature verifier now emits structured diagnostics/metrics via `VexAttestationDiagnostics`; attestation verification results flow into metric labels and logs.
> 2025-11-06 07:12Z: Export verifier builds unblocked; Excititor worker + web service test suites pass with diagnostics wiring (`dotnet test` invocations succeed with staged libssl1.1).
> 2025-11-06 07:55Z: Paused after documenting OpenSSL shim usage; follow-up automation tracked under `DEVOPS-OPENSSL-11-001/002`.
> Remark (2025-10-22): Added verifier implementation + metrics/tests; next steps include wiring into WebService/Worker flows and expanding negative-path coverage. > Remark (2025-10-22): Added verifier implementation + metrics/tests; next steps include wiring into WebService/Worker flows and expanding negative-path coverage.

View File

@@ -98,6 +98,7 @@ public sealed class VexExportEngine : IExportEngine
cached.PolicyDigest, cached.PolicyDigest,
cached.ConsensusDigest, cached.ConsensusDigest,
cached.ScoreDigest, cached.ScoreDigest,
cached.QuietProvenance,
cached.Attestation, cached.Attestation,
cached.SizeBytes); cached.SizeBytes);
} }

View File

@@ -130,7 +130,7 @@ internal static class VexExportEnvelopeBuilder
} }
} }
internal sealed record VexExportEnvelopeContext( public sealed record VexExportEnvelopeContext(
ImmutableArray<VexConsensus> Consensus, ImmutableArray<VexConsensus> Consensus,
string ConsensusCanonicalJson, string ConsensusCanonicalJson,
VexContentAddress ConsensusDigest, VexContentAddress ConsensusDigest,

View File

@@ -280,7 +280,7 @@ public sealed class VexMirrorBundlePublisher : IVexMirrorBundlePublisher
ToRelativePath(mirrorRoot, manifestPath), ToRelativePath(mirrorRoot, manifestPath),
manifestBytes.LongLength, manifestBytes.LongLength,
ComputeDigest(manifestBytes), ComputeDigest(manifestBytes),
signature: null); Signature: null);
var bundleDescriptor = manifestDocument.Bundle with var bundleDescriptor = manifestDocument.Bundle with
{ {
@@ -298,7 +298,7 @@ public sealed class VexMirrorBundlePublisher : IVexMirrorBundlePublisher
manifestDocument.DomainId, manifestDocument.DomainId,
manifestDocument.DisplayName, manifestDocument.DisplayName,
manifestDocument.GeneratedAt, manifestDocument.GeneratedAt,
manifestDocument.Exports.Length, manifestDocument.Exports.Count,
manifestDescriptor, manifestDescriptor,
bundleDescriptor, bundleDescriptor,
exportKeys)); exportKeys));
@@ -474,6 +474,11 @@ public sealed class VexMirrorBundlePublisher : IVexMirrorBundlePublisher
private JsonMirrorSigningContext PrepareSigningContext(MirrorSigningOptions signingOptions) private JsonMirrorSigningContext PrepareSigningContext(MirrorSigningOptions signingOptions)
{ {
if (_cryptoRegistry is null)
{
throw new InvalidOperationException("Mirror signing requires a crypto provider registry to be configured.");
}
var algorithm = string.IsNullOrWhiteSpace(signingOptions.Algorithm) var algorithm = string.IsNullOrWhiteSpace(signingOptions.Algorithm)
? SignatureAlgorithms.Es256 ? SignatureAlgorithms.Es256
: signingOptions.Algorithm.Trim(); : signingOptions.Algorithm.Trim();
@@ -496,7 +501,7 @@ public sealed class VexMirrorBundlePublisher : IVexMirrorBundlePublisher
var provider = ResolveProvider(algorithm, providerHint); var provider = ResolveProvider(algorithm, providerHint);
var signingKey = LoadSigningKey(signingOptions, provider, algorithm); var signingKey = LoadSigningKey(signingOptions, provider, algorithm);
provider.UpsertSigningKey(signingKey); provider.UpsertSigningKey(signingKey);
resolved = _cryptoRegistry.ResolveSigner(CryptoCapability.Signing, algorithm, new CryptoKeyReference(keyId, provider.Name), provider.Name); resolved = _cryptoRegistry!.ResolveSigner(CryptoCapability.Signing, algorithm, new CryptoKeyReference(keyId, provider.Name), provider.Name);
} }
return new JsonMirrorSigningContext(resolved.Signer, algorithm, resolved.ProviderName, _timeProvider); return new JsonMirrorSigningContext(resolved.Signer, algorithm, resolved.ProviderName, _timeProvider);

View File

@@ -4,7 +4,7 @@
|----|--------|----------|------------|-------------|---------------| |----|--------|----------|------------|-------------|---------------|
| SCAN-REPLAY-186-001 | TODO | Scanner WebService Guild | REPLAY-CORE-185-001 | Implement scan `record` mode producing replay manifests/bundles, capture policy/feed/tool hashes, and update `docs/modules/scanner/architecture.md` referencing `docs/replay/DETERMINISTIC_REPLAY.md` Section 6. | API/worker integration tests cover record mode; docs merged; replay artifacts stored per spec. | | SCAN-REPLAY-186-001 | TODO | Scanner WebService Guild | REPLAY-CORE-185-001 | Implement scan `record` mode producing replay manifests/bundles, capture policy/feed/tool hashes, and update `docs/modules/scanner/architecture.md` referencing `docs/replay/DETERMINISTIC_REPLAY.md` Section 6. | API/worker integration tests cover record mode; docs merged; replay artifacts stored per spec. |
| SCANNER-SURFACE-02 | DONE (2025-11-05) | Scanner WebService Guild | SURFACE-FS-02 | Publish Surface.FS pointers (CAS URIs, manifests) via scan/report APIs and update attestation metadata.<br>2025-11-05: Surface pointers projected through scan/report endpoints, orchestrator samples + DSSE fixtures refreshed with manifest block, readiness tests updated to use validator stub. | OpenAPI updated; clients regenerated; integration tests validate pointer presence and tenancy. | | SCANNER-SURFACE-02 | DONE (2025-11-05) | Scanner WebService Guild | SURFACE-FS-02 | Publish Surface.FS pointers (CAS URIs, manifests) via scan/report APIs and update attestation metadata.<br>2025-11-05: Surface pointers projected through scan/report endpoints, orchestrator samples + DSSE fixtures refreshed with manifest block, readiness tests updated to use validator stub. | OpenAPI updated; clients regenerated; integration tests validate pointer presence and tenancy. |
| SCANNER-ENV-02 | DOING (2025-11-02) | Scanner WebService Guild, Ops Guild | SURFACE-ENV-02 | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration.<br>2025-11-02: Cache root resolution switched to helper; feature flag bindings updated; Helm/Compose updates pending review.<br>2025-11-05 14:55Z: Aligning readiness checks, docs, and Helm/Compose templates with Surface.Env outputs and planning test coverage for configuration fallbacks.<br>2025-11-06 17:05Z: Surface.Env documentation/README refreshed; warning catalogue captured for ops handoff. | Service uses helper; env table documented; helm/compose templates updated. | | SCANNER-ENV-02 | TODO (2025-11-06) | Scanner WebService Guild, Ops Guild | SURFACE-ENV-02 | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration.<br>2025-11-02: Cache root resolution switched to helper; feature flag bindings updated; Helm/Compose updates pending review.<br>2025-11-05 14:55Z: Aligning readiness checks, docs, and Helm/Compose templates with Surface.Env outputs and planning test coverage for configuration fallbacks.<br>2025-11-06 17:05Z: Surface.Env documentation/README refreshed; warning catalogue captured for ops handoff.<br>2025-11-06 07:45Z: Helm values (dev/stage/prod/airgap/mirror) and Compose examples updated with `SCANNER_SURFACE_*` defaults plus rollout warning note in `deploy/README.md`.<br>2025-11-06 07:55Z: Paused; follow-up automation captured under `DEVOPS-OPENSSL-11-001/002` and pending Surface.Env readiness tests. | Service uses helper; env table documented; helm/compose templates updated. |
> 2025-11-05 19:18Z: Added configurator to project wiring and unit test ensuring Surface.Env cache root is honoured. > 2025-11-05 19:18Z: Added configurator to project wiring and unit test ensuring Surface.Env cache root is honoured.
| SCANNER-SECRETS-02 | DOING (2025-11-02) | Scanner WebService Guild, Security Guild | SURFACE-SECRETS-02 | Replace ad-hoc secret wiring with Surface.Secrets for report/export operations (registry and CAS tokens).<br>2025-11-02: Export/report flows now depend on Surface.Secrets stub; integration tests in progress. | Secrets fetched through shared provider; unit/integration tests cover rotation + failure cases. | | SCANNER-SECRETS-02 | DOING (2025-11-02) | Scanner WebService Guild, Security Guild | SURFACE-SECRETS-02 | Replace ad-hoc secret wiring with Surface.Secrets for report/export operations (registry and CAS tokens).<br>2025-11-02: Export/report flows now depend on Surface.Secrets stub; integration tests in progress. | Secrets fetched through shared provider; unit/integration tests cover rotation + failure cases. |
| SCANNER-EVENTS-16-301 | BLOCKED (2025-10-26) | Scanner WebService Guild | ORCH-SVC-38-101, NOTIFY-SVC-38-001 | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). | Tests assert envelope schema + orchestrator publish; Notifier consumer harness passes; docs updated with new event contract. Blocked by .NET 10 preview OpenAPI/Auth dependency drift preventing `dotnet test` completion. | | SCANNER-EVENTS-16-301 | BLOCKED (2025-10-26) | Scanner WebService Guild | ORCH-SVC-38-101, NOTIFY-SVC-38-001 | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). | Tests assert envelope schema + orchestrator publish; Notifier consumer harness passes; docs updated with new event contract. Blocked by .NET 10 preview OpenAPI/Auth dependency drift preventing `dotnet test` completion. |

View File

@@ -4,6 +4,6 @@
|----|--------|----------|------------|-------------|---------------| |----|--------|----------|------------|-------------|---------------|
| SCAN-REPLAY-186-002 | TODO | Scanner Worker Guild | REPLAY-CORE-185-001 | Enforce deterministic analyzer execution when consuming replay input bundles, emit layer Merkle metadata, and author `docs/modules/scanner/deterministic-execution.md` summarising invariants from `docs/replay/DETERMINISTIC_REPLAY.md` Section 4. | Replay mode analyzers pass determinism tests; new doc merged; integration fixtures updated. | | SCAN-REPLAY-186-002 | TODO | Scanner Worker Guild | REPLAY-CORE-185-001 | Enforce deterministic analyzer execution when consuming replay input bundles, emit layer Merkle metadata, and author `docs/modules/scanner/deterministic-execution.md` summarising invariants from `docs/replay/DETERMINISTIC_REPLAY.md` Section 4. | Replay mode analyzers pass determinism tests; new doc merged; integration fixtures updated. |
| SCANNER-SURFACE-01 | DOING (2025-11-02) | Scanner Worker Guild | SURFACE-FS-02 | Persist Surface.FS manifests after analyzer stages, including layer CAS metadata and EntryTrace fragments.<br>2025-11-02: Draft Surface.FS manifests emitted for sample scans; telemetry counters under review. | Integration tests prove cache entries exist; telemetry counters exported. | | SCANNER-SURFACE-01 | DOING (2025-11-02) | Scanner Worker Guild | SURFACE-FS-02 | Persist Surface.FS manifests after analyzer stages, including layer CAS metadata and EntryTrace fragments.<br>2025-11-02: Draft Surface.FS manifests emitted for sample scans; telemetry counters under review. | Integration tests prove cache entries exist; telemetry counters exported. |
| SCANNER-ENV-01 | DOING (2025-11-02) | Scanner Worker Guild | SURFACE-ENV-02 | Replace ad-hoc environment reads with `StellaOps.Scanner.Surface.Env` helpers for cache roots and CAS endpoints.<br>2025-11-02: Worker bootstrap now resolves cache roots via helper; warning path documented; smoke tests running.<br>2025-11-05 14:55Z: Extending helper usage into cache/secrets configuration, updating worker validator wiring, and drafting docs/tests for new Surface.Env outputs.<br>2025-11-06 17:05Z: README/design docs updated with warning catalogue; startup logging guidance captured for ops runbooks. | Worker boots with helper; misconfiguration warnings documented; smoke tests updated. | | SCANNER-ENV-01 | TODO (2025-11-06) | Scanner Worker Guild | SURFACE-ENV-02 | Replace ad-hoc environment reads with `StellaOps.Scanner.Surface.Env` helpers for cache roots and CAS endpoints.<br>2025-11-02: Worker bootstrap now resolves cache roots via helper; warning path documented; smoke tests running.<br>2025-11-05 14:55Z: Extending helper usage into cache/secrets configuration, updating worker validator wiring, and drafting docs/tests for new Surface.Env outputs.<br>2025-11-06 17:05Z: README/design docs updated with warning catalogue; startup logging guidance captured for ops runbooks.<br>2025-11-06 07:45Z: Helm/Compose env profiles (dev/stage/prod/airgap/mirror) now seed `SCANNER_SURFACE_*` defaults to keep worker cache roots aligned with Surface.Env helpers.<br>2025-11-06 07:55Z: Paused; pending automation tracked via `DEVOPS-OPENSSL-11-001/002` and Surface.Env test fixtures. | Worker boots with helper; misconfiguration warnings documented; smoke tests updated. |
> 2025-11-05 19:18Z: Bound `SurfaceCacheOptions` root directory to resolved Surface.Env settings and added unit coverage around the configurator. > 2025-11-05 19:18Z: Bound `SurfaceCacheOptions` root directory to resolved Surface.Env settings and added unit coverage around the configurator.
| SCANNER-SECRETS-01 | DOING (2025-11-02) | Scanner Worker Guild, Security Guild | SURFACE-SECRETS-02 | Adopt `StellaOps.Scanner.Surface.Secrets` for registry/CAS credentials during scan execution.<br>2025-11-02: Surface.Secrets provider wired for CAS token retrieval; integration tests added. | Secrets fetched via shared provider; legacy secret code removed; integration tests cover rotation. | | SCANNER-SECRETS-01 | DOING (2025-11-02) | Scanner Worker Guild, Security Guild | SURFACE-SECRETS-02 | Adopt `StellaOps.Scanner.Surface.Secrets` for registry/CAS credentials during scan execution.<br>2025-11-02: Surface.Secrets provider wired for CAS token retrieval; integration tests added. | Secrets fetched via shared provider; legacy secret code removed; integration tests cover rotation. |

View File

@@ -0,0 +1,23 @@
{
"schema": "scheduler-impact-index@1",
"generatedAt": "2025-10-01T00:00:00Z",
"image": {
"repository": "registry.stellaops.test/team/sample-service",
"digest": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"tag": "1.0.0"
},
"components": [
{
"purl": "pkg:docker/sample-service@1.0.0",
"usage": [
"runtime"
]
},
{
"purl": "pkg:pypi/requests@2.31.0",
"usage": [
"usedByEntrypoint"
]
}
]
}

View File

@@ -8,6 +8,7 @@
<ProjectReference Include="../StellaOps.Scheduler.Models/StellaOps.Scheduler.Models.csproj" /> <ProjectReference Include="../StellaOps.Scheduler.Models/StellaOps.Scheduler.Models.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Fixtures\**\*.json" />
<EmbeddedResource Include="..\..\samples\scanner\images\**\bom-index.json" <EmbeddedResource Include="..\..\samples\scanner\images\**\bom-index.json"
Link="Fixtures\%(RecursiveDir)%(Filename)%(Extension)" /> Link="Fixtures\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,52 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Scheduler.ImpactIndex;
using StellaOps.Scheduler.Models;
using Xunit;
namespace StellaOps.Scheduler.WebService.Tests;
public sealed class ImpactIndexFixtureTests
{
[Fact]
public void FixtureDirectoryExists()
{
var fixtureDirectory = GetFixtureDirectory();
Assert.True(Directory.Exists(fixtureDirectory), $"Fixture directory not found: {fixtureDirectory}");
var files = Directory.EnumerateFiles(fixtureDirectory, "bom-index.json", SearchOption.AllDirectories).ToArray();
Assert.NotEmpty(files);
var sampleFile = Path.Combine(fixtureDirectory, "sample", "bom-index.json");
Assert.Contains(sampleFile, files);
}
[Fact]
public async Task FixtureImpactIndexLoadsSampleImage()
{
var fixtureDirectory = GetFixtureDirectory();
var options = new ImpactIndexStubOptions
{
FixtureDirectory = fixtureDirectory,
SnapshotId = "tests/impact-index-stub"
};
var index = new FixtureImpactIndex(options, TimeProvider.System, NullLogger<FixtureImpactIndex>.Instance);
var selector = new Selector(SelectorScope.AllImages);
var impactSet = await index.ResolveAllAsync(selector, usageOnly: false);
Assert.True(impactSet.Total > 0, "Expected the fixture impact index to load at least one image.");
}
private static string GetFixtureDirectory()
{
var assemblyLocation = typeof(SchedulerWebApplicationFactory).Assembly.Location;
var assemblyDirectory = Path.GetDirectoryName(assemblyLocation)
?? AppContext.BaseDirectory;
return Path.GetFullPath(Path.Combine(assemblyDirectory, "seed-data", "impact-index"));
}
}

View File

@@ -11,16 +11,16 @@ using Microsoft.Extensions.DependencyInjection;
using StellaOps.Scheduler.Models; using StellaOps.Scheduler.Models;
using StellaOps.Scheduler.Queue; using StellaOps.Scheduler.Queue;
using StellaOps.Scheduler.Storage.Mongo.Repositories; using StellaOps.Scheduler.Storage.Mongo.Repositories;
namespace StellaOps.Scheduler.WebService.Tests; namespace StellaOps.Scheduler.WebService.Tests;
public sealed class RunEndpointTests : IClassFixture<WebApplicationFactory<Program>> public sealed class RunEndpointTests : IClassFixture<WebApplicationFactory<Program>>
{ {
private readonly WebApplicationFactory<Program> _factory; private readonly WebApplicationFactory<Program> _factory;
public RunEndpointTests(WebApplicationFactory<Program> factory) public RunEndpointTests(WebApplicationFactory<Program> factory)
{ {
_factory = factory; _factory = factory;
} }
[Fact] [Fact]
@@ -100,13 +100,13 @@ public sealed class RunEndpointTests : IClassFixture<WebApplicationFactory<Progr
var scheduleId = scheduleJson.GetProperty("schedule").GetProperty("id").GetString(); var scheduleId = scheduleJson.GetProperty("schedule").GetProperty("id").GetString();
Assert.False(string.IsNullOrEmpty(scheduleId)); Assert.False(string.IsNullOrEmpty(scheduleId));
var previewResponse = await client.PostAsJsonAsync("/api/v1/scheduler/runs/preview", new var previewResponse = await client.PostAsJsonAsync("/api/v1/scheduler/runs/preview", new
{ {
scheduleId, scheduleId,
usageOnly = true, usageOnly = true,
sampleSize = 3 sampleSize = 3
}); });
previewResponse.EnsureSuccessStatusCode(); previewResponse.EnsureSuccessStatusCode();
var preview = await previewResponse.Content.ReadFromJsonAsync<JsonElement>(); var preview = await previewResponse.Content.ReadFromJsonAsync<JsonElement>();
Assert.True(preview.GetProperty("total").GetInt32() >= 0); Assert.True(preview.GetProperty("total").GetInt32() >= 0);

View File

@@ -1,11 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.Scheduler.WebService.Options; using StellaOps.Scheduler.WebService.Options;
using StellaOps.Scheduler.WebService.Runs; using StellaOps.Scheduler.WebService.Runs;
using StellaOps.Scheduler.ImpactIndex;
namespace StellaOps.Scheduler.WebService.Tests; namespace StellaOps.Scheduler.WebService.Tests;
@@ -15,6 +18,8 @@ public sealed class SchedulerWebApplicationFactory : WebApplicationFactory<Progr
{ {
builder.ConfigureAppConfiguration((_, configuration) => builder.ConfigureAppConfiguration((_, configuration) =>
{ {
var fixtureDirectory = GetFixtureDirectory();
configuration.AddInMemoryCollection(new[] configuration.AddInMemoryCollection(new[]
{ {
new KeyValuePair<string, string?>("Scheduler:Authority:Enabled", "false"), new KeyValuePair<string, string?>("Scheduler:Authority:Enabled", "false"),
@@ -27,12 +32,22 @@ public sealed class SchedulerWebApplicationFactory : WebApplicationFactory<Progr
new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:Enabled", "true"), new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:Enabled", "true"),
new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:HmacSecret", "excitor-secret"), new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:HmacSecret", "excitor-secret"),
new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:RateLimitRequests", "20"), new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:RateLimitRequests", "20"),
new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:RateLimitWindowSeconds", "60") new KeyValuePair<string, string?>("Scheduler:Events:Webhooks:Excitor:RateLimitWindowSeconds", "60"),
new KeyValuePair<string, string?>("Scheduler:ImpactIndex:FixtureDirectory", fixtureDirectory)
}); });
}); });
builder.ConfigureServices(services => builder.ConfigureServices(services =>
{ {
var fixtureDirectory = GetFixtureDirectory();
services.RemoveAll<ImpactIndexStubOptions>();
services.AddSingleton(new ImpactIndexStubOptions
{
FixtureDirectory = fixtureDirectory,
SnapshotId = "tests/impact-index-stub"
});
services.Configure<SchedulerEventsOptions>(options => services.Configure<SchedulerEventsOptions>(options =>
{ {
options.Webhooks ??= new SchedulerInboundWebhooksOptions(); options.Webhooks ??= new SchedulerInboundWebhooksOptions();
@@ -52,4 +67,14 @@ public sealed class SchedulerWebApplicationFactory : WebApplicationFactory<Progr
}); });
}); });
} }
private static string GetFixtureDirectory()
{
var assemblyLocation = typeof(SchedulerWebApplicationFactory).Assembly.Location;
var assemblyDirectory = Path.GetDirectoryName(assemblyLocation)
?? AppContext.BaseDirectory;
var fixtureDirectory = Path.Combine(assemblyDirectory, "seed-data", "impact-index");
return Path.GetFullPath(fixtureDirectory);
}
} }

View File

@@ -18,4 +18,9 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../StellaOps.Scheduler.WebService/StellaOps.Scheduler.WebService.csproj" /> <ProjectReference Include="../../StellaOps.Scheduler.WebService/StellaOps.Scheduler.WebService.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="seed-data/impact-index/**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,23 @@
{
"schema": "scheduler-impact-index@1",
"generatedAt": "2025-10-01T00:00:00Z",
"image": {
"repository": "registry.stellaops.test/team/sample-service",
"digest": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"tag": "1.0.0"
},
"components": [
{
"purl": "pkg:docker/sample-service@1.0.0",
"usage": [
"runtime"
]
},
{
"purl": "pkg:pypi/requests@2.31.0",
"usage": [
"usedByEntrypoint"
]
}
]
}

View File

@@ -0,0 +1,239 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.Logging;
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
namespace StellaOps.TaskRunner.Infrastructure.Execution;
/// <summary>
/// Stores pack run artifacts on the local file system so they can be mirrored to the eventual remote store.
/// </summary>
public sealed class FilesystemPackRunArtifactUploader : IPackRunArtifactUploader
{
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
{
WriteIndented = true
};
private readonly string rootPath;
private readonly ILogger<FilesystemPackRunArtifactUploader> logger;
private readonly TimeProvider timeProvider;
public FilesystemPackRunArtifactUploader(
string rootPath,
TimeProvider? timeProvider,
ILogger<FilesystemPackRunArtifactUploader> logger)
{
ArgumentException.ThrowIfNullOrWhiteSpace(rootPath);
this.rootPath = Path.GetFullPath(rootPath);
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.timeProvider = timeProvider ?? TimeProvider.System;
Directory.CreateDirectory(this.rootPath);
}
public async Task UploadAsync(
PackRunExecutionContext context,
PackRunState state,
IReadOnlyList<TaskPackPlanOutput> outputs,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(state);
ArgumentNullException.ThrowIfNull(outputs);
if (outputs.Count == 0)
{
return;
}
var destinationRoot = Path.Combine(rootPath, SanitizeFileName(context.RunId));
var filesRoot = Path.Combine(destinationRoot, "files");
var expressionsRoot = Path.Combine(destinationRoot, "expressions");
Directory.CreateDirectory(destinationRoot);
var manifest = new ArtifactManifest(
context.RunId,
timeProvider.GetUtcNow(),
new List<ArtifactRecord>(outputs.Count));
foreach (var output in outputs)
{
cancellationToken.ThrowIfCancellationRequested();
var record = await ProcessOutputAsync(
context,
output,
destinationRoot,
filesRoot,
expressionsRoot,
cancellationToken).ConfigureAwait(false);
manifest.Outputs.Add(record);
}
var manifestPath = Path.Combine(destinationRoot, "artifact-manifest.json");
await using (var stream = File.Open(manifestPath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await JsonSerializer.SerializeAsync(stream, manifest, SerializerOptions, cancellationToken)
.ConfigureAwait(false);
}
logger.LogInformation(
"Pack run {RunId} artifact manifest written to {Path} with {Count} output entries.",
context.RunId,
manifestPath,
manifest.Outputs.Count);
}
private async Task<ArtifactRecord> ProcessOutputAsync(
PackRunExecutionContext context,
TaskPackPlanOutput output,
string destinationRoot,
string filesRoot,
string expressionsRoot,
CancellationToken cancellationToken)
{
var sourcePath = ResolveString(output.Path);
var expressionNode = ResolveExpression(output.Expression);
var status = "skipped";
string? storedPath = null;
string? notes = null;
if (IsFileOutput(output))
{
if (string.IsNullOrWhiteSpace(sourcePath))
{
status = "unresolved";
notes = "Output path requires runtime value.";
}
else if (!File.Exists(sourcePath))
{
status = "missing";
notes = $"Source file '{sourcePath}' not found.";
logger.LogWarning(
"Pack run {RunId} output {Output} referenced missing file {Path}.",
context.RunId,
output.Name,
sourcePath);
}
else
{
Directory.CreateDirectory(filesRoot);
var destinationPath = Path.Combine(filesRoot, DetermineDestinationFileName(output, sourcePath));
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
await CopyFileAsync(sourcePath, destinationPath, cancellationToken).ConfigureAwait(false);
storedPath = GetRelativePath(destinationPath, destinationRoot);
status = "copied";
logger.LogInformation(
"Pack run {RunId} output {Output} copied to {Destination}.",
context.RunId,
output.Name,
destinationPath);
}
}
if (expressionNode is not null)
{
Directory.CreateDirectory(expressionsRoot);
var expressionPath = Path.Combine(
expressionsRoot,
$"{SanitizeFileName(output.Name)}.json");
var json = expressionNode.ToJsonString(SerializerOptions);
await File.WriteAllTextAsync(expressionPath, json, cancellationToken).ConfigureAwait(false);
storedPath ??= GetRelativePath(expressionPath, destinationRoot);
status = status == "copied" ? "copied" : "materialized";
}
return new ArtifactRecord(
output.Name,
output.Type,
sourcePath,
storedPath,
status,
notes);
}
private static async Task CopyFileAsync(string sourcePath, string destinationPath, CancellationToken cancellationToken)
{
await using var source = File.Open(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
await using var destination = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None);
await source.CopyToAsync(destination, cancellationToken).ConfigureAwait(false);
}
private static bool IsFileOutput(TaskPackPlanOutput output)
=> string.Equals(output.Type, "file", StringComparison.OrdinalIgnoreCase);
private static string DetermineDestinationFileName(TaskPackPlanOutput output, string sourcePath)
{
var extension = Path.GetExtension(sourcePath);
var baseName = SanitizeFileName(output.Name);
if (!string.IsNullOrWhiteSpace(extension) &&
!baseName.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
{
return baseName + extension;
}
return baseName;
}
private static string? ResolveString(TaskPackPlanParameterValue? parameter)
{
if (parameter is null || parameter.RequiresRuntimeValue || parameter.Value is null)
{
return null;
}
if (parameter.Value is JsonValue jsonValue && jsonValue.TryGetValue<string>(out var value))
{
return value;
}
return null;
}
private static JsonNode? ResolveExpression(TaskPackPlanParameterValue? parameter)
{
if (parameter is null || parameter.RequiresRuntimeValue)
{
return null;
}
return parameter.Value;
}
private static string SanitizeFileName(string value)
{
var result = value;
foreach (var invalid in Path.GetInvalidFileNameChars())
{
result = result.Replace(invalid, '_');
}
return string.IsNullOrWhiteSpace(result) ? "output" : result;
}
private static string GetRelativePath(string path, string root)
=> Path.GetRelativePath(root, path)
.Replace('\\', '/');
private sealed record ArtifactManifest(string RunId, DateTimeOffset UploadedAt, List<ArtifactRecord> Outputs);
private sealed record ArtifactRecord(
string Name,
string Type,
string? SourcePath,
string? StoredPath,
string Status,
string? Notes);
}

View File

@@ -0,0 +1,138 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Infrastructure.Execution;
using Xunit;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilesystemPackRunArtifactUploaderTests : IDisposable
{
private readonly string artifactsRoot;
public FilesystemPackRunArtifactUploaderTests()
{
artifactsRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("n"));
}
[Fact]
public async Task CopiesFileOutputs()
{
var sourceFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():n}.txt");
await File.WriteAllTextAsync(sourceFile, "artifact-content", TestContext.Current.CancellationToken);
var uploader = CreateUploader();
var output = CreateFileOutput("bundle", sourceFile);
var context = CreateContext();
var state = CreateState(context);
await uploader.UploadAsync(context, state, new[] { output }, TestContext.Current.CancellationToken);
var runPath = Path.Combine(artifactsRoot, context.RunId);
var filesDirectory = Path.Combine(runPath, "files");
var copiedFiles = Directory.GetFiles(filesDirectory);
Assert.Single(copiedFiles);
Assert.Equal("bundle.txt", Path.GetFileName(copiedFiles[0]));
Assert.Equal("artifact-content", await File.ReadAllTextAsync(copiedFiles[0], TestContext.Current.CancellationToken));
var manifest = await ReadManifestAsync(runPath);
Assert.Single(manifest.Outputs);
Assert.Equal("copied", manifest.Outputs[0].Status);
Assert.Equal("files/bundle.txt", manifest.Outputs[0].StoredPath);
}
[Fact]
public async Task RecordsMissingFilesWithoutThrowing()
{
var uploader = CreateUploader();
var output = CreateFileOutput("missing", Path.Combine(Path.GetTempPath(), "does-not-exist.txt"));
var context = CreateContext();
var state = CreateState(context);
await uploader.UploadAsync(context, state, new[] { output }, TestContext.Current.CancellationToken);
var manifest = await ReadManifestAsync(Path.Combine(artifactsRoot, context.RunId));
Assert.Equal("missing", manifest.Outputs[0].Status);
}
[Fact]
public async Task WritesExpressionOutputsAsJson()
{
var uploader = CreateUploader();
var output = CreateExpressionOutput("metadata", JsonNode.Parse("""{"foo":"bar"}""")!);
var context = CreateContext();
var state = CreateState(context);
await uploader.UploadAsync(context, state, new[] { output }, TestContext.Current.CancellationToken);
var expressionPath = Path.Combine(artifactsRoot, context.RunId, "expressions", "metadata.json");
Assert.True(File.Exists(expressionPath));
var manifest = await ReadManifestAsync(Path.Combine(artifactsRoot, context.RunId));
Assert.Equal("materialized", manifest.Outputs[0].Status);
Assert.Equal("expressions/metadata.json", manifest.Outputs[0].StoredPath);
}
private FilesystemPackRunArtifactUploader CreateUploader()
=> new(artifactsRoot, TimeProvider.System, NullLogger<FilesystemPackRunArtifactUploader>.Instance);
private static TaskPackPlanOutput CreateFileOutput(string name, string path)
=> new(
name,
Type: "file",
Path: new TaskPackPlanParameterValue(JsonValue.Create(path), null, null, false),
Expression: null);
private static TaskPackPlanOutput CreateExpressionOutput(string name, JsonNode expression)
=> new(
name,
Type: "object",
Path: null,
Expression: new TaskPackPlanParameterValue(expression, null, null, false));
private static PackRunExecutionContext CreateContext()
=> new("run-" + Guid.NewGuid().ToString("n"), CreatePlan(), DateTimeOffset.UtcNow);
private static PackRunState CreateState(PackRunExecutionContext context)
=> PackRunState.Create(
runId: context.RunId,
planHash: context.Plan.Hash,
context.Plan,
failurePolicy: new TaskPackPlanFailurePolicy(1, 1, false),
requestedAt: DateTimeOffset.UtcNow,
steps: new Dictionary<string, PackRunStepStateRecord>(StringComparer.Ordinal),
timestamp: DateTimeOffset.UtcNow);
private static TaskPackPlan CreatePlan()
{
return new TaskPackPlan(
new TaskPackPlanMetadata("sample-pack", "1.0.0", null, Array.Empty<string>()),
new Dictionary<string, JsonNode?>(StringComparer.Ordinal),
Array.Empty<TaskPackPlanStep>(),
hash: "hash",
approvals: Array.Empty<TaskPackPlanApproval>(),
secrets: Array.Empty<TaskPackPlanSecret>(),
outputs: Array.Empty<TaskPackPlanOutput>(),
failurePolicy: new TaskPackPlanFailurePolicy(1, 1, false));
}
private static async Task<ArtifactManifestModel> ReadManifestAsync(string runPath)
{
var json = await File.ReadAllTextAsync(Path.Combine(runPath, "artifact-manifest.json"), TestContext.Current.CancellationToken);
return JsonSerializer.Deserialize<ArtifactManifestModel>(json, new JsonSerializerOptions(JsonSerializerDefaults.Web))!;
}
public void Dispose()
{
if (Directory.Exists(artifactsRoot))
{
Directory.Delete(artifactsRoot, recursive: true);
}
}
private sealed record ArtifactManifestModel(string RunId, DateTimeOffset UploadedAt, List<ArtifactRecordModel> Outputs);
private sealed record ArtifactRecordModel(string Name, string Type, string? SourcePath, string? StoredPath, string Status, string? Notes);
}

View File

@@ -51,7 +51,13 @@ builder.Services.AddSingleton<IPackRunStepExecutor, NoopPackRunStepExecutor>();
builder.Services.AddSingleton<PackRunExecutionGraphBuilder>(); builder.Services.AddSingleton<PackRunExecutionGraphBuilder>();
builder.Services.AddSingleton<PackRunSimulationEngine>(); builder.Services.AddSingleton<PackRunSimulationEngine>();
builder.Services.AddSingleton<PackRunProcessor>(); builder.Services.AddSingleton<PackRunProcessor>();
builder.Services.AddSingleton<IPackRunArtifactUploader, LoggingPackRunArtifactUploader>(); builder.Services.AddSingleton<IPackRunArtifactUploader>(sp =>
{
var options = sp.GetRequiredService<IOptions<PackRunWorkerOptions>>().Value;
var timeProvider = sp.GetService<TimeProvider>();
var logger = sp.GetRequiredService<ILogger<FilesystemPackRunArtifactUploader>>();
return new FilesystemPackRunArtifactUploader(options.ArtifactsPath, timeProvider, logger);
});
builder.Services.AddHostedService<PackRunWorkerService>(); builder.Services.AddHostedService<PackRunWorkerService>();
var host = builder.Build(); var host = builder.Build();

View File

@@ -4,11 +4,13 @@ public sealed class PackRunWorkerOptions
{ {
public TimeSpan IdleDelay { get; set; } = TimeSpan.FromSeconds(1); public TimeSpan IdleDelay { get; set; } = TimeSpan.FromSeconds(1);
public string QueuePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "queue"); public string QueuePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "queue");
public string ArchivePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "queue", "archive"); public string ArchivePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "queue", "archive");
public string ApprovalStorePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "approvals"); public string ApprovalStorePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "approvals");
public string RunStatePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "state", "runs"); public string RunStatePath { get; set; } = Path.Combine(AppContext.BaseDirectory, "state", "runs");
public string ArtifactsPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "artifacts");
} }

View File

@@ -1,5 +1,6 @@
using Google.Cloud.Kms.V1; using Google.Cloud.Kms.V1;
using Google.Protobuf; using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
namespace StellaOps.Cryptography.Kms; namespace StellaOps.Cryptography.Kms;

View File

@@ -271,7 +271,7 @@ internal sealed class Pkcs11InteropFacade : IPkcs11Facade
} }
catch catch
{ {
# ignore logout failures // ignore logout failures
} }
} }

View File

@@ -64,9 +64,8 @@ public sealed class Pkcs11Options
/// <summary> /// <summary>
/// Gets or sets an optional factory for advanced facade injection (testing, custom providers). /// Gets or sets an optional factory for advanced facade injection (testing, custom providers).
/// </summary> /// </summary>
public Func<IServiceProvider, IPkcs11Facade>? FacadeFactory { get; set; } internal Func<IServiceProvider, IPkcs11Facade>? FacadeFactory { get; set; }
private static TimeSpan EnsurePositive(TimeSpan value, TimeSpan fallback) private static TimeSpan EnsurePositive(TimeSpan value, TimeSpan fallback)
=> value <= TimeSpan.Zero ? fallback : value; => value <= TimeSpan.Zero ? fallback : value;
} }

View File

@@ -5,3 +5,22 @@ These binaries (libcrypto.so.1.1 and libssl.so.1.1) are bundled for Mongo2Go-bas
Source package: https://launchpad.net/ubuntu/+archive/primary/+files/libssl1.1_1.1.1f-1ubuntu2_amd64.deb Source package: https://launchpad.net/ubuntu/+archive/primary/+files/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
Licensing follows the OpenSSL and SSLeay licenses that accompany the upstream package. Licensing follows the OpenSSL and SSLeay licenses that accompany the upstream package.
## Usage
1. Point `LD_LIBRARY_PATH` at this directory before running any test suite that spins up Mongo2Go:
```bash
export LD_LIBRARY_PATH="$(git rev-parse --show-toplevel)/tests/native/openssl-1.1/linux-x64:${LD_LIBRARY_PATH:-}"
```
The helper in `tests/shared/OpenSslLegacyShim.cs` will append the path automatically when the tests run, but exporting the variable up-front avoids surprises when using custom harnesses (e.g., `dotnet test` filters).
2. Example one-shot command:
```bash
LD_LIBRARY_PATH="$(pwd)/tests/native/openssl-1.1/linux-x64" \
dotnet test src/Excititor/__Tests/StellaOps.Excititor.Worker.Tests/StellaOps.Excititor.Worker.Tests.csproj --nologo
```
3. CI runners should add the same directory to their environment or place the binaries on a globally accessible library path.