:ref_type:tag:ref:.*` |
-
-**OIDC Issuer:** Use `${CI_SERVER_URL}` for self-hosted GitLab, or `https://gitlab.com` for GitLab.com.
-
-## Example Pipeline
-
-See `examples/example-pipeline.gitlab-ci.yml` for a complete pipeline example.
-
-## Troubleshooting
-
-### OIDC Token Not Available
-
-Ensure your job has `id_tokens` configured:
-
-```yaml
-my-job:
- id_tokens:
- STELLAOPS_OIDC_TOKEN:
- aud: sigstore
-```
-
-### Permission Denied
-
-Check that:
-1. The project has OIDC enabled (Settings > CI/CD > Token Access)
-2. Protected branch/tag settings if using protected pipelines
-
-### Verification Fails
-
-Common issues:
-- Identity pattern doesn't match (check `ref_type` and `ref`)
-- Wrong issuer (use `${CI_SERVER_URL}` for self-hosted)
-- Signature was created by different branch/tag
-
-## Resources
-
-- [Keyless Signing Guide](../../docs/modules/signer/guides/keyless-signing.md)
-- [Identity Constraints](../../docs/guides/identity-constraints.md)
-- [GitLab OIDC Documentation](https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html)
diff --git a/devops/gitlab/examples/.gitlab-ci-stellaops.yml b/devops/gitlab/examples/.gitlab-ci-stellaops.yml
deleted file mode 100644
index 7d3e15dd0..000000000
--- a/devops/gitlab/examples/.gitlab-ci-stellaops.yml
+++ /dev/null
@@ -1,305 +0,0 @@
-# deploy/gitlab/examples/.gitlab-ci-stellaops.yml
-# StellaOps Keyless Signing Templates for GitLab CI
-#
-# Include this file in your .gitlab-ci.yml to enable keyless signing:
-#
-# include:
-# - project: 'stella-ops/templates'
-# file: 'deploy/gitlab/examples/.gitlab-ci-stellaops.yml'
-#
-# sign-image:
-# extends: .stellaops-sign
-# variables:
-# ARTIFACT_DIGEST: $CI_REGISTRY_IMAGE@sha256:...
-# ARTIFACT_TYPE: image
-#
-# See: docs/modules/signer/guides/keyless-signing.md
-
-# ==============================================================================
-# Base Configuration
-# ==============================================================================
-
-variables:
- STELLAOPS_URL: "https://api.stella-ops.org"
- STELLAOPS_CLI_VERSION: "latest"
-
-# ==============================================================================
-# Keyless Signing Job Template
-# ==============================================================================
-
-.stellaops-sign:
- image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
- id_tokens:
- STELLAOPS_OIDC_TOKEN:
- aud: sigstore
- variables:
- # Required - must be set by extending job
- ARTIFACT_DIGEST: ""
- # Optional - defaults to 'image'
- ARTIFACT_TYPE: "image"
- # Optional - include in Rekor transparency log
- INCLUDE_REKOR: "true"
- # Optional - push attestation to registry
- PUSH_ATTESTATION: "true"
- before_script:
- - |
- if [[ -z "${ARTIFACT_DIGEST}" ]]; then
- echo "ERROR: ARTIFACT_DIGEST must be set"
- exit 1
- fi
- script:
- - |
- set -euo pipefail
-
- SIGN_ARGS=(
- --keyless
- --artifact "${ARTIFACT_DIGEST}"
- --type "${ARTIFACT_TYPE}"
- --output json
- )
-
- if [[ "${INCLUDE_REKOR}" == "true" ]]; then
- SIGN_ARGS+=(--rekor)
- fi
-
- echo "Signing artifact: ${ARTIFACT_DIGEST}"
- RESULT=$(stella attest sign "${SIGN_ARGS[@]}")
-
- # Extract outputs for downstream jobs
- ATTESTATION_DIGEST=$(echo "$RESULT" | jq -r '.attestationDigest')
- REKOR_UUID=$(echo "$RESULT" | jq -r '.rekorUuid // empty')
- CERT_IDENTITY=$(echo "$RESULT" | jq -r '.certificateIdentity // empty')
-
- echo "ATTESTATION_DIGEST=${ATTESTATION_DIGEST}" >> sign.env
- echo "REKOR_UUID=${REKOR_UUID}" >> sign.env
- echo "CERTIFICATE_IDENTITY=${CERT_IDENTITY}" >> sign.env
-
- echo "Attestation created: ${ATTESTATION_DIGEST}"
- if [[ -n "${REKOR_UUID}" ]]; then
- echo "Rekor UUID: ${REKOR_UUID}"
- fi
-
- # Push attestation if requested
- if [[ "${PUSH_ATTESTATION}" == "true" ]]; then
- echo "Pushing attestation to registry..."
- stella attest push \
- --attestation "${ATTESTATION_DIGEST}" \
- --registry "${CI_REGISTRY_IMAGE}"
- fi
- artifacts:
- reports:
- dotenv: sign.env
-
-# ==============================================================================
-# Verification Job Template
-# ==============================================================================
-
-.stellaops-verify:
- image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
- variables:
- # Required - must be set by extending job
- ARTIFACT_DIGEST: ""
- CERTIFICATE_IDENTITY: ""
- CERTIFICATE_OIDC_ISSUER: "https://gitlab.com"
- # Optional - verification settings
- REQUIRE_REKOR: "true"
- STRICT: "true"
- REQUIRE_SBOM: "false"
- REQUIRE_VERDICT: "false"
- before_script:
- - |
- if [[ -z "${ARTIFACT_DIGEST}" ]]; then
- echo "ERROR: ARTIFACT_DIGEST must be set"
- exit 1
- fi
- if [[ -z "${CERTIFICATE_IDENTITY}" ]]; then
- echo "ERROR: CERTIFICATE_IDENTITY must be set"
- exit 1
- fi
- script:
- - |
- set -euo pipefail
-
- VERIFY_ARGS=(
- --artifact "${ARTIFACT_DIGEST}"
- --certificate-identity "${CERTIFICATE_IDENTITY}"
- --certificate-oidc-issuer "${CERTIFICATE_OIDC_ISSUER}"
- --output json
- )
-
- if [[ "${REQUIRE_REKOR}" == "true" ]]; then
- VERIFY_ARGS+=(--require-rekor)
- fi
-
- if [[ "${REQUIRE_SBOM}" == "true" ]]; then
- VERIFY_ARGS+=(--require-sbom)
- fi
-
- if [[ "${REQUIRE_VERDICT}" == "true" ]]; then
- VERIFY_ARGS+=(--require-verdict)
- fi
-
- echo "Verifying artifact: ${ARTIFACT_DIGEST}"
- echo "Expected identity: ${CERTIFICATE_IDENTITY}"
-
- set +e
- RESULT=$(stella attest verify "${VERIFY_ARGS[@]}" 2>&1)
- EXIT_CODE=$?
- set -e
-
- VERIFIED=$(echo "$RESULT" | jq -r '.valid // false')
- ATTESTATION_COUNT=$(echo "$RESULT" | jq -r '.attestationCount // 0')
-
- echo "VERIFIED=${VERIFIED}" >> verify.env
- echo "ATTESTATION_COUNT=${ATTESTATION_COUNT}" >> verify.env
-
- echo "Verified: ${VERIFIED}"
- echo "Attestations found: ${ATTESTATION_COUNT}"
-
- if [[ "$VERIFIED" != "true" ]]; then
- echo "Verification issues:"
- echo "$RESULT" | jq -r '.issues[]? | " - \(.code): \(.message)"'
-
- if [[ "${STRICT}" == "true" ]]; then
- echo "ERROR: Verification failed in strict mode"
- exit 1
- fi
- fi
- artifacts:
- reports:
- dotenv: verify.env
-
-# ==============================================================================
-# SBOM Generation and Signing Template
-# ==============================================================================
-
-.stellaops-sbom:
- image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
- id_tokens:
- STELLAOPS_OIDC_TOKEN:
- aud: sigstore
- variables:
- # Required - image to generate SBOM for
- IMAGE: ""
- # Optional - SBOM format
- SBOM_FORMAT: "cyclonedx-json"
- # Optional - output file
- SBOM_OUTPUT: "sbom.json"
- before_script:
- - |
- if [[ -z "${IMAGE}" ]]; then
- echo "ERROR: IMAGE must be set"
- exit 1
- fi
- script:
- - |
- set -euo pipefail
-
- echo "Generating SBOM for: ${IMAGE}"
-
- # Generate SBOM
- stella sbom generate \
- --image "${IMAGE}" \
- --format "${SBOM_FORMAT}" \
- --output "${SBOM_OUTPUT}"
-
- # Calculate digest
- SBOM_DIGEST="sha256:$(sha256sum "${SBOM_OUTPUT}" | cut -d' ' -f1)"
- echo "SBOM digest: ${SBOM_DIGEST}"
-
- # Sign SBOM
- echo "Signing SBOM..."
- RESULT=$(stella attest sign \
- --keyless \
- --artifact "${SBOM_DIGEST}" \
- --type sbom \
- --rekor \
- --output json)
-
- ATTESTATION_DIGEST=$(echo "$RESULT" | jq -r '.attestationDigest')
- REKOR_UUID=$(echo "$RESULT" | jq -r '.rekorUuid // empty')
-
- echo "SBOM_DIGEST=${SBOM_DIGEST}" >> sbom.env
- echo "SBOM_ATTESTATION_DIGEST=${ATTESTATION_DIGEST}" >> sbom.env
- echo "SBOM_REKOR_UUID=${REKOR_UUID}" >> sbom.env
-
- # Attach to image
- echo "Attaching SBOM to image..."
- stella attest attach \
- --image "${IMAGE}" \
- --attestation "${ATTESTATION_DIGEST}" \
- --type sbom
-
- echo "SBOM signed and attached successfully"
- artifacts:
- paths:
- - ${SBOM_OUTPUT}
- reports:
- dotenv: sbom.env
-
-# ==============================================================================
-# Policy Verdict Template
-# ==============================================================================
-
-.stellaops-verdict:
- image: stella-ops/cli:${STELLAOPS_CLI_VERSION}
- id_tokens:
- STELLAOPS_OIDC_TOKEN:
- aud: sigstore
- variables:
- # Required - image to evaluate
- IMAGE: ""
- # Optional - policy pack ID
- POLICY: "default"
- # Optional - fail on block verdict
- FAIL_ON_BLOCK: "true"
- before_script:
- - |
- if [[ -z "${IMAGE}" ]]; then
- echo "ERROR: IMAGE must be set"
- exit 1
- fi
- script:
- - |
- set -euo pipefail
-
- echo "Evaluating policy '${POLICY}' for: ${IMAGE}"
-
- RESULT=$(stella policy evaluate \
- --image "${IMAGE}" \
- --policy "${POLICY}" \
- --output json)
-
- VERDICT=$(echo "$RESULT" | jq -r '.verdict')
- VERDICT_DIGEST=$(echo "$RESULT" | jq -r '.verdictDigest')
- PASSED=$(echo "$RESULT" | jq -r '.passed')
-
- echo "Verdict: ${VERDICT}"
- echo "Passed: ${PASSED}"
-
- # Sign verdict
- echo "Signing verdict..."
- SIGN_RESULT=$(stella attest sign \
- --keyless \
- --artifact "${VERDICT_DIGEST}" \
- --type verdict \
- --rekor \
- --output json)
-
- ATTESTATION_DIGEST=$(echo "$SIGN_RESULT" | jq -r '.attestationDigest')
- REKOR_UUID=$(echo "$SIGN_RESULT" | jq -r '.rekorUuid // empty')
-
- echo "VERDICT=${VERDICT}" >> verdict.env
- echo "VERDICT_DIGEST=${VERDICT_DIGEST}" >> verdict.env
- echo "VERDICT_PASSED=${PASSED}" >> verdict.env
- echo "VERDICT_ATTESTATION_DIGEST=${ATTESTATION_DIGEST}" >> verdict.env
- echo "VERDICT_REKOR_UUID=${REKOR_UUID}" >> verdict.env
-
- # Check if we should fail
- if [[ "${PASSED}" != "true" && "${FAIL_ON_BLOCK}" == "true" ]]; then
- echo "ERROR: Policy verdict is ${VERDICT} - blocking deployment"
- exit 1
- fi
- artifacts:
- reports:
- dotenv: verdict.env
diff --git a/devops/gitlab/examples/example-pipeline.gitlab-ci.yml b/devops/gitlab/examples/example-pipeline.gitlab-ci.yml
deleted file mode 100644
index 687e69613..000000000
--- a/devops/gitlab/examples/example-pipeline.gitlab-ci.yml
+++ /dev/null
@@ -1,195 +0,0 @@
-# deploy/gitlab/examples/example-pipeline.gitlab-ci.yml
-# Example GitLab CI pipeline with StellaOps keyless signing
-#
-# This example demonstrates:
-# - Building and pushing a container image
-# - Generating and signing SBOM
-# - Evaluating and signing policy verdict
-# - Verification gate before deployment
-#
-# To use, copy this file to your repository's .gitlab-ci.yml
-
-include:
- - local: 'deploy/gitlab/examples/.gitlab-ci-stellaops.yml'
- # Or include from StellaOps templates project:
- # - project: 'stella-ops/templates'
- # file: 'deploy/gitlab/examples/.gitlab-ci-stellaops.yml'
-
-stages:
- - build
- - scan
- - sign
- - verify
- - deploy
-
-variables:
- DOCKER_TLS_CERTDIR: "/certs"
- IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
-
-# ==============================================================================
-# Build Stage
-# ==============================================================================
-
-build:
- stage: build
- image: docker:24
- services:
- - docker:24-dind
- before_script:
- - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- script:
- - |
- docker build -t ${IMAGE} .
- docker push ${IMAGE}
-
- # Get digest
- DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ${IMAGE} | cut -d@ -f2)
- echo "IMAGE_DIGEST=${DIGEST}" >> build.env
- echo "IMAGE_REF=${CI_REGISTRY_IMAGE}@${DIGEST}" >> build.env
- artifacts:
- reports:
- dotenv: build.env
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- - if: $CI_COMMIT_TAG
-
-# ==============================================================================
-# Scan Stage
-# ==============================================================================
-
-generate-sbom:
- stage: scan
- extends: .stellaops-sbom
- needs:
- - build
- variables:
- IMAGE: ${IMAGE_REF}
- SBOM_FORMAT: "cyclonedx-json"
- SBOM_OUTPUT: "sbom.cdx.json"
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- - if: $CI_COMMIT_TAG
-
-vulnerability-scan:
- stage: scan
- image: stella-ops/cli:latest
- needs:
- - build
- script:
- - |
- stella scan vulnerability \
- --image "${IMAGE_REF}" \
- --output json > vulnerabilities.json
-
- # Extract summary
- CRITICAL=$(jq '.summary.critical // 0' vulnerabilities.json)
- HIGH=$(jq '.summary.high // 0' vulnerabilities.json)
-
- echo "Critical: ${CRITICAL}, High: ${HIGH}"
-
- if [[ "${CRITICAL}" -gt 0 ]]; then
- echo "WARNING: ${CRITICAL} critical vulnerabilities found"
- fi
- artifacts:
- paths:
- - vulnerabilities.json
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- - if: $CI_COMMIT_TAG
-
-# ==============================================================================
-# Sign Stage
-# ==============================================================================
-
-sign-image:
- stage: sign
- extends: .stellaops-sign
- needs:
- - build
- variables:
- ARTIFACT_DIGEST: ${IMAGE_DIGEST}
- ARTIFACT_TYPE: "image"
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- - if: $CI_COMMIT_TAG
-
-evaluate-policy:
- stage: sign
- extends: .stellaops-verdict
- needs:
- - build
- - vulnerability-scan
- variables:
- IMAGE: ${IMAGE_REF}
- POLICY: "production"
- FAIL_ON_BLOCK: "false" # Don't fail here, let verify stage handle it
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- - if: $CI_COMMIT_TAG
-
-# ==============================================================================
-# Verify Stage
-# ==============================================================================
-
-verify-for-deployment:
- stage: verify
- extends: .stellaops-verify
- needs:
- - build
- - sign-image
- - generate-sbom
- - evaluate-policy
- variables:
- ARTIFACT_DIGEST: ${IMAGE_DIGEST}
- CERTIFICATE_IDENTITY: "project_path:${CI_PROJECT_PATH}:ref_type:branch:ref:${CI_COMMIT_REF_NAME}"
- CERTIFICATE_OIDC_ISSUER: "${CI_SERVER_URL}"
- REQUIRE_SBOM: "true"
- REQUIRE_VERDICT: "true"
- STRICT: "true"
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- - if: $CI_COMMIT_TAG
-
-# ==============================================================================
-# Deploy Stage
-# ==============================================================================
-
-deploy-staging:
- stage: deploy
- needs:
- - build
- - verify-for-deployment
- environment:
- name: staging
- url: https://staging.example.com
- script:
- - |
- echo "Deploying ${IMAGE_REF} to staging"
- echo "All attestations verified:"
- echo " - Image signature: ${ATTESTATION_DIGEST}"
- echo " - SBOM: ${SBOM_ATTESTATION_DIGEST}"
- echo " - Policy verdict: ${VERDICT_ATTESTATION_DIGEST}"
-
- # Add your deployment commands here
- # kubectl set image deployment/app app=${IMAGE_REF}
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-
-deploy-production:
- stage: deploy
- needs:
- - build
- - verify-for-deployment
- - deploy-staging
- environment:
- name: production
- url: https://example.com
- script:
- - |
- echo "Deploying ${IMAGE_REF} to production"
- echo "Policy verdict: ${VERDICT}"
-
- # Add your deployment commands here
- rules:
- - if: $CI_COMMIT_TAG
- when: manual
diff --git a/devops/gitlab/stellaops-gate-example.gitlab-ci.yml b/devops/gitlab/stellaops-gate-example.gitlab-ci.yml
deleted file mode 100644
index adcd77963..000000000
--- a/devops/gitlab/stellaops-gate-example.gitlab-ci.yml
+++ /dev/null
@@ -1,306 +0,0 @@
-# -----------------------------------------------------------------------------
-# stellaops-gate-example.gitlab-ci.yml
-# Sprint: SPRINT_20251226_001_BE_cicd_gate_integration
-# Task: CICD-GATE-08 - GitLab CI example workflow using stella gate evaluate
-# Description: Example GitLab CI configuration for StellaOps release gate integration
-# -----------------------------------------------------------------------------
-#
-# This configuration demonstrates how to integrate StellaOps release gates into
-# your GitLab CI/CD pipeline. The gate evaluates security drift between your
-# current build and the approved baseline, blocking releases that introduce new
-# reachable vulnerabilities.
-#
-# Usage:
-# Include this file in your .gitlab-ci.yml:
-# include:
-# - project: 'stellaops/ci-templates'
-# file: '/templates/stellaops-gate.gitlab-ci.yml'
-#
-# Prerequisites:
-# 1. STELLAOPS_API_TOKEN variable configured in CI/CD settings
-# 2. STELLAOPS_BACKEND_URL variable configured (or use default)
-# 3. Container image built and pushed to registry
-#
-# Exit codes:
-# 0 = Pass - Release may proceed
-# 1 = Warn - Release may proceed with warnings (configurable)
-# 2 = Fail - Release blocked due to security policy violation
-#
-
-variables:
- STELLAOPS_BACKEND_URL: ${STELLAOPS_BACKEND_URL:-https://stellaops.internal}
- STELLAOPS_CLI_VERSION: "latest"
- # Registry configuration
- REGISTRY: ${CI_REGISTRY}
- IMAGE_NAME: ${CI_REGISTRY_IMAGE}
-
-stages:
- - build
- - scan
- - gate
- - deploy
-
-# -----------------------------------------------------------------------------
-# Build Stage: Build and push container image
-# -----------------------------------------------------------------------------
-build:
- stage: build
- image: docker:24
- services:
- - docker:24-dind
- variables:
- DOCKER_TLS_CERTDIR: "/certs"
- before_script:
- - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- script:
- - |
- # Build with BuildKit for better caching
- export DOCKER_BUILDKIT=1
-
- # Generate image tag based on commit
- IMAGE_TAG="${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"
-
- # Build and push
- docker build \
- --label "org.opencontainers.image.revision=${CI_COMMIT_SHA}" \
- --label "org.opencontainers.image.source=${CI_PROJECT_URL}" \
- -t "${IMAGE_TAG}" \
- .
-
- docker push "${IMAGE_TAG}"
-
- # Get the digest
- IMAGE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${IMAGE_TAG}" | cut -d'@' -f2)
- echo "IMAGE_DIGEST=${IMAGE_DIGEST}" >> build.env
- echo "IMAGE_REF=${CI_REGISTRY_IMAGE}@${IMAGE_DIGEST}" >> build.env
- artifacts:
- reports:
- dotenv: build.env
-
-# -----------------------------------------------------------------------------
-# Gate Stage: Evaluate StellaOps release gate
-# -----------------------------------------------------------------------------
-.stellaops-gate-base:
- stage: gate
- image: alpine:3.19
- variables:
- # Baseline strategy: auto-detect based on branch
- BASELINE_STRATEGY: "auto"
- # Allow warnings to pass by default
- ALLOW_WARNINGS: "true"
- before_script:
- - |
- # Install dependencies
- apk add --no-cache curl jq bash
-
- # Install StellaOps CLI
- curl -sSL https://get.stella-ops.org/cli | bash
- export PATH="$HOME/.stellaops/bin:$PATH"
-
- # Verify installation
- stella --version
-
-stellaops-gate:
- extends: .stellaops-gate-base
- needs:
- - job: build
- artifacts: true
- script:
- - |
- # Determine baseline strategy based on branch
- if [ "$BASELINE_STRATEGY" = "auto" ]; then
- case "$CI_COMMIT_REF_NAME" in
- main|master)
- BASELINE="production"
- ;;
- release/*)
- BASELINE="last-approved"
- ;;
- *)
- BASELINE="previous-build"
- ;;
- esac
- else
- BASELINE="$BASELINE_STRATEGY"
- fi
-
- echo "============================================"
- echo "StellaOps Release Gate Evaluation"
- echo "============================================"
- echo "Image Digest: ${IMAGE_DIGEST}"
- echo "Baseline Strategy: ${BASELINE}"
- echo "Branch: ${CI_COMMIT_REF_NAME}"
- echo "============================================"
-
- # Run gate evaluation
- set +e
- RESULT=$(stella gate evaluate \
- --image "${IMAGE_DIGEST}" \
- --baseline "${BASELINE}" \
- --output json \
- --ci-context "gitlab-ci" \
- --repository "${CI_PROJECT_PATH}" \
- --tag "${CI_COMMIT_SHORT_SHA}" \
- 2>&1)
- EXIT_CODE=$?
- set -e
-
- # Parse results
- DECISION_ID=$(echo "$RESULT" | jq -r '.decisionId // "unknown"')
- STATUS=$(echo "$RESULT" | jq -r '.status // "unknown"')
- SUMMARY=$(echo "$RESULT" | jq -r '.summary // "No summary"')
-
- # Store for downstream jobs
- echo "GATE_DECISION_ID=${DECISION_ID}" >> gate.env
- echo "GATE_STATUS=${STATUS}" >> gate.env
- echo "GATE_EXIT_CODE=${EXIT_CODE}" >> gate.env
-
- # Display results
- echo ""
- echo "============================================"
- echo "Gate Result: ${STATUS}"
- echo "Decision ID: ${DECISION_ID}"
- echo "============================================"
- echo "${SUMMARY}"
- echo "============================================"
-
- # Handle exit codes
- case $EXIT_CODE in
- 0)
- echo "Gate PASSED - Release may proceed"
- ;;
- 1)
- echo "Gate PASSED WITH WARNINGS"
- if [ "$ALLOW_WARNINGS" = "true" ]; then
- echo "Warnings allowed - continuing pipeline"
- exit 0
- else
- echo "Warnings not allowed - blocking pipeline"
- exit 1
- fi
- ;;
- 2)
- echo "Gate BLOCKED - Security policy violation"
- echo "Review the gate decision for details:"
- echo "${STELLAOPS_BACKEND_URL}/gates/decisions/${DECISION_ID}"
- exit 2
- ;;
- *)
- echo "Gate evaluation error (exit code: $EXIT_CODE)"
- exit $EXIT_CODE
- ;;
- esac
- artifacts:
- reports:
- dotenv: gate.env
- rules:
- - if: $CI_COMMIT_BRANCH
- - if: $CI_MERGE_REQUEST_IID
-
-# -----------------------------------------------------------------------------
-# Gate Override: Manual override for blocked releases
-# -----------------------------------------------------------------------------
-stellaops-gate-override:
- extends: .stellaops-gate-base
- needs:
- - job: build
- artifacts: true
- - job: stellaops-gate
- artifacts: true
- script:
- - |
- if [ "$GATE_STATUS" != "Fail" ]; then
- echo "Override not needed - gate status is ${GATE_STATUS}"
- exit 0
- fi
-
- echo "============================================"
- echo "StellaOps Gate Override Request"
- echo "============================================"
- echo "Original Decision ID: ${GATE_DECISION_ID}"
- echo "Override requested by: ${GITLAB_USER_LOGIN}"
- echo "Justification: ${OVERRIDE_JUSTIFICATION}"
- echo "============================================"
-
- if [ -z "$OVERRIDE_JUSTIFICATION" ]; then
- echo "ERROR: OVERRIDE_JUSTIFICATION variable must be set"
- exit 1
- fi
-
- # Request override with justification
- stella gate evaluate \
- --image "${IMAGE_DIGEST}" \
- --baseline "last-approved" \
- --allow-override \
- --justification "${OVERRIDE_JUSTIFICATION}" \
- --ci-context "gitlab-ci-override" \
- --repository "${CI_PROJECT_PATH}" \
- --tag "${CI_COMMIT_SHORT_SHA}"
- rules:
- - if: $CI_COMMIT_BRANCH
- when: manual
- allow_failure: true
- environment:
- name: security-override
- action: prepare
-
-# -----------------------------------------------------------------------------
-# Deploy Stage: Deploy to staging (only if gate passed)
-# -----------------------------------------------------------------------------
-deploy-staging:
- stage: deploy
- image: alpine:3.19
- needs:
- - job: build
- artifacts: true
- - job: stellaops-gate
- artifacts: true
- script:
- - |
- echo "Deploying ${IMAGE_REF} to staging..."
-
- # Verify gate passed
- if [ "$GATE_STATUS" != "Pass" ] && [ "$GATE_STATUS" != "Warn" ]; then
- echo "ERROR: Gate did not pass (status: ${GATE_STATUS})"
- exit 1
- fi
-
- # Add your deployment commands here
- # Example: kubectl set image deployment/app app=${IMAGE_REF}
- echo "Deployment complete!"
- environment:
- name: staging
- url: https://staging.example.com
- rules:
- - if: $CI_COMMIT_BRANCH == "main"
- - if: $CI_COMMIT_BRANCH =~ /^release\//
-
-# -----------------------------------------------------------------------------
-# Deploy Stage: Deploy to production (requires manual approval)
-# -----------------------------------------------------------------------------
-deploy-production:
- stage: deploy
- image: alpine:3.19
- needs:
- - job: build
- artifacts: true
- - job: stellaops-gate
- artifacts: true
- script:
- - |
- echo "Deploying ${IMAGE_REF} to production..."
-
- # Verify gate passed (warnings not allowed for production)
- if [ "$GATE_STATUS" != "Pass" ]; then
- echo "ERROR: Production deployment requires Pass status (got: ${GATE_STATUS})"
- exit 1
- fi
-
- # Add your production deployment commands here
- echo "Production deployment complete!"
- environment:
- name: production
- url: https://example.com
- rules:
- - if: $CI_COMMIT_BRANCH == "main"
- when: manual
diff --git a/devops/helm/stellaops/values-airgap.yaml b/devops/helm/stellaops/values-airgap.yaml
index 192cf08de..428839f45 100644
--- a/devops/helm/stellaops/values-airgap.yaml
+++ b/devops/helm/stellaops/values-airgap.yaml
@@ -53,9 +53,8 @@ configMaps:
data:
notify.yaml: |
storage:
- driver: mongo
- connectionString: "mongodb://notify-mongo.prod.svc.cluster.local:27017"
- database: "stellaops_notify"
+ driver: postgres
+ connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
commandTimeoutSeconds: 60
authority:
@@ -104,7 +103,9 @@ services:
port: 8440
env:
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
- STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
+ STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
+ STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
+ STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
STELLAOPS_AUTHORITY__ALLOWANONYMOUSFALLBACK: "false"
signer:
image: registry.stella-ops.org/stellaops/signer@sha256:ddbbd664a42846cea6b40fca6465bc679b30f72851158f300d01a8571c5478fc
@@ -113,23 +114,27 @@ services:
env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "file:///offline/poe/introspect.json"
- SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
+ SIGNER__STORAGE__DRIVER: "postgres"
+ SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
+ SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:1ff0a3124d66d3a2702d8e421df40fbd98cc75cb605d95510598ebbae1433c50
service:
port: 8442
env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
- ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
+ ATTESTOR__STORAGE__DRIVER: "postgres"
+ ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
+ ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:29e2e1a0972707e092cbd3d370701341f9fec2aa9316fb5d8100480f2a1c76b5
service:
port: 8445
env:
- CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
- CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
- CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-airgap"
- CONCELIER__STORAGE__S3__SECRETACCESSKEY: "airgap-minio-secret"
+ CONCELIER__STORAGE__DRIVER: "postgres"
+ CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
+ CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
+ CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "00:45:00"
@@ -144,16 +149,17 @@ services:
service:
port: 8444
env:
- SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "false"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -169,16 +175,17 @@ services:
scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5
env:
- SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "false"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -203,6 +210,8 @@ services:
port: 8446
env:
DOTNET_ENVIRONMENT: Production
+ NOTIFY__QUEUE__DRIVER: "valkey"
+ NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
configMounts:
- name: notify-config
mountPath: /app/etc/notify.yaml
@@ -212,7 +221,8 @@ services:
image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68
env:
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
- EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-airgap:stellaops-airgap@stellaops-mongo:27017"
+ EXCITITOR__STORAGE__DRIVER: "postgres"
+ EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
advisory-ai-web:
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2-airgap
service:
@@ -254,42 +264,38 @@ services:
targetPort: 8443
env:
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
- mongo:
+
+ # Infrastructure services
+ postgres:
class: infrastructure
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
service:
- port: 27017
- command:
- - mongod
- - --bind_ip_all
+ port: 5432
env:
- MONGO_INITDB_ROOT_USERNAME: stellaops-airgap
- MONGO_INITDB_ROOT_PASSWORD: stellaops-airgap
+ POSTGRES_USER: stellaops
+ POSTGRES_PASSWORD: stellaops
+ POSTGRES_DB: stellaops
volumeMounts:
- - name: mongo-data
- mountPath: /data/db
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
volumeClaims:
- - name: mongo-data
- claimName: stellaops-mongo-data
- minio:
+ - name: postgres-data
+ claimName: stellaops-postgres-data
+ valkey:
class: infrastructure
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ image: docker.io/valkey/valkey:9.0.1-alpine
service:
- port: 9000
+ port: 6379
command:
- - server
- - /data
- - --console-address
- - :9001
- env:
- MINIO_ROOT_USER: stellaops-airgap
- MINIO_ROOT_PASSWORD: airgap-minio-secret
+ - valkey-server
+ - --appendonly
+ - "yes"
volumeMounts:
- - name: minio-data
+ - name: valkey-data
mountPath: /data
volumeClaims:
- - name: minio-data
- claimName: stellaops-minio-data
+ - name: valkey-data
+ claimName: stellaops-valkey-data
rustfs:
class: infrastructure
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
@@ -310,19 +316,3 @@ services:
volumeClaims:
- name: rustfs-data
claimName: stellaops-rustfs-data
- nats:
- class: infrastructure
- image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
- service:
- port: 4222
- command:
- - -js
- - -sd
- - /data
- volumeMounts:
- - name: nats-data
- mountPath: /data
- volumeClaims:
- - name: nats-data
- claimName: stellaops-nats-data
-
diff --git a/devops/helm/stellaops/values-dev.yaml b/devops/helm/stellaops/values-dev.yaml
index 28bd8adbb..06e5f9e45 100644
--- a/devops/helm/stellaops/values-dev.yaml
+++ b/devops/helm/stellaops/values-dev.yaml
@@ -21,9 +21,8 @@ configMaps:
data:
notify.yaml: |
storage:
- driver: mongo
- connectionString: "mongodb://notify-mongo.dev.svc.cluster.local:27017"
- database: "stellaops_notify_dev"
+ driver: postgres
+ connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
commandTimeoutSeconds: 30
authority:
@@ -63,6 +62,7 @@ configMaps:
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "false"
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "false"
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
+
services:
authority:
image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
@@ -70,7 +70,9 @@ services:
port: 8440
env:
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
- STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
+ STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
+ STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
+ STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
signer:
@@ -80,23 +82,27 @@ services:
env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "https://licensing.svc.local/introspect"
- SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
+ SIGNER__STORAGE__DRIVER: "postgres"
+ SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
+ SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
service:
port: 8442
env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
- ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
+ ATTESTOR__STORAGE__DRIVER: "postgres"
+ ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
+ ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
service:
port: 8445
env:
- CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
- CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
- CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops"
- CONCELIER__STORAGE__S3__SECRETACCESSKEY: "dev-minio-secret"
+ CONCELIER__STORAGE__DRIVER: "postgres"
+ CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
+ CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
+ CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
volumeMounts:
- name: concelier-jobs
@@ -109,16 +115,17 @@ services:
service:
port: 8444
env:
- SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "false"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -134,16 +141,17 @@ services:
scanner-worker:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
env:
- SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "false"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -157,6 +165,8 @@ services:
port: 8446
env:
DOTNET_ENVIRONMENT: Development
+ NOTIFY__QUEUE__DRIVER: "valkey"
+ NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
configMounts:
- name: notify-config
mountPath: /app/etc/notify.yaml
@@ -166,7 +176,8 @@ services:
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
env:
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
- EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops:stellaops@stellaops-mongo:27017"
+ EXCITITOR__STORAGE__DRIVER: "postgres"
+ EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
advisory-ai-web:
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.10.0-edge
service:
@@ -207,41 +218,37 @@ services:
port: 8443
env:
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
- mongo:
+
+ # Infrastructure services
+ postgres:
class: infrastructure
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
service:
- port: 27017
- command:
- - mongod
- - --bind_ip_all
+ port: 5432
env:
- MONGO_INITDB_ROOT_USERNAME: stellaops
- MONGO_INITDB_ROOT_PASSWORD: stellaops
+ POSTGRES_USER: stellaops
+ POSTGRES_PASSWORD: stellaops
+ POSTGRES_DB: stellaops
volumeMounts:
- - name: mongo-data
- mountPath: /data/db
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
volumes:
- - name: mongo-data
+ - name: postgres-data
emptyDir: {}
- minio:
+ valkey:
class: infrastructure
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ image: docker.io/valkey/valkey:9.0.1-alpine
service:
- port: 9000
+ port: 6379
command:
- - server
- - /data
- - --console-address
- - :9001
- env:
- MINIO_ROOT_USER: stellaops
- MINIO_ROOT_PASSWORD: dev-minio-secret
+ - valkey-server
+ - --appendonly
+ - "yes"
volumeMounts:
- - name: minio-data
+ - name: valkey-data
mountPath: /data
volumes:
- - name: minio-data
+ - name: valkey-data
emptyDir: {}
rustfs:
class: infrastructure
@@ -257,19 +264,3 @@ services:
volumes:
- name: rustfs-data
emptyDir: {}
- nats:
- class: infrastructure
- image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
- service:
- port: 4222
- command:
- - -js
- - -sd
- - /data
- volumeMounts:
- - name: nats-data
- mountPath: /data
- volumes:
- - name: nats-data
- emptyDir: {}
-
diff --git a/devops/helm/stellaops/values-export.yaml b/devops/helm/stellaops/values-export.yaml
index 4f1c0aafd..35c918652 100644
--- a/devops/helm/stellaops/values-export.yaml
+++ b/devops/helm/stellaops/values-export.yaml
@@ -3,10 +3,10 @@ exportcenter:
repository: registry.stella-ops.org/export-center
tag: latest
objectStorage:
- endpoint: http://minio:9000
+ endpoint: http://rustfs:8080
bucket: export-prod
- accessKeySecret: exportcenter-minio
- secretKeySecret: exportcenter-minio
+ accessKeySecret: exportcenter-rustfs
+ secretKeySecret: exportcenter-rustfs
signing:
kmsKey: exportcenter-kms
kmsRegion: us-east-1
diff --git a/devops/helm/stellaops/values-mirror.yaml b/devops/helm/stellaops/values-mirror.yaml
index 803a0eca7..bd7639a8d 100644
--- a/devops/helm/stellaops/values-mirror.yaml
+++ b/devops/helm/stellaops/values-mirror.yaml
@@ -106,28 +106,28 @@ configMaps:
proxy_cache off;
}
- location / {
- return 404;
- }
-
-
- policy-engine-activation:
- data:
- STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "true"
- STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "true"
- STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
-
-services:
+ location / {
+ return 404;
+ }
+
+
+ policy-engine-activation:
+ data:
+ STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "true"
+ STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "true"
+ STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
+
+services:
concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
service:
port: 8445
env:
ASPNETCORE_URLS: "http://+:8445"
- CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops_mirror:mirror-password@stellaops-mongo:27017/concelier?authSource=admin"
- CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
- CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-mirror"
- CONCELIER__STORAGE__S3__SECRETACCESSKEY: "mirror-minio-secret"
+ CONCELIER__STORAGE__DRIVER: "postgres"
+ CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
+ CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
+ CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
CONCELIER__TELEMETRY__SERVICENAME: "stellaops-concelier-mirror"
CONCELIER__MIRROR__ENABLED: "true"
CONCELIER__MIRROR__EXPORTROOT: "/exports/json"
@@ -183,8 +183,8 @@ services:
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
env:
ASPNETCORE_URLS: "http://+:8448"
- EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops_mirror:mirror-password@stellaops-mongo:27017/excititor?authSource=admin"
- EXCITITOR__STORAGE__MONGO__DATABASENAME: "excititor"
+ EXCITITOR__STORAGE__DRIVER: "postgres"
+ EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
EXCITITOR__ARTIFACTS__FILESYSTEM__ROOT: "/exports"
EXCITITOR__ARTIFACTS__FILESYSTEM__OVERWRITEEXISTING: "false"
EXCITITOR__MIRROR__DOMAINS__0__ID: "primary"
@@ -220,43 +220,59 @@ services:
secret:
secretName: excititor-mirror-auth
- mongo:
+ # Infrastructure services
+ postgres:
class: infrastructure
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
service:
- port: 27017
- command:
- - mongod
- - --bind_ip_all
+ port: 5432
env:
- MONGO_INITDB_ROOT_USERNAME: "stellaops_mirror"
- MONGO_INITDB_ROOT_PASSWORD: "mirror-password"
+ POSTGRES_USER: stellaops
+ POSTGRES_PASSWORD: stellaops
+ POSTGRES_DB: stellaops
volumeMounts:
- - name: mongo-data
- mountPath: /data/db
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
volumeClaims:
- - name: mongo-data
- claimName: mirror-mongo-data
+ - name: postgres-data
+ claimName: mirror-postgres-data
- minio:
+ valkey:
class: infrastructure
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ image: docker.io/valkey/valkey:9.0.1-alpine
service:
- port: 9000
+ port: 6379
command:
- - server
- - /data
- - --console-address
- - :9001
- env:
- MINIO_ROOT_USER: "stellaops-mirror"
- MINIO_ROOT_PASSWORD: "mirror-minio-secret"
+ - valkey-server
+ - --appendonly
+ - "yes"
volumeMounts:
- - name: minio-data
+ - name: valkey-data
mountPath: /data
volumeClaims:
- - name: minio-data
- claimName: mirror-minio-data
+ - name: valkey-data
+ claimName: mirror-valkey-data
+
+ rustfs:
+ class: infrastructure
+ image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
+ service:
+ port: 8080
+ command:
+ - serve
+ - --listen
+ - 0.0.0.0:8080
+ - --root
+ - /data
+ env:
+ RUSTFS__LOG__LEVEL: info
+ RUSTFS__STORAGE__PATH: /data
+ volumeMounts:
+ - name: rustfs-data
+ mountPath: /data
+ volumeClaims:
+ - name: rustfs-data
+ claimName: mirror-rustfs-data
mirror-gateway:
image: docker.io/library/nginx@sha256:208b70eefac13ee9be00e486f79c695b15cef861c680527171a27d253d834be9
diff --git a/devops/helm/stellaops/values-prod.yaml b/devops/helm/stellaops/values-prod.yaml
index 7536c6646..4427dc686 100644
--- a/devops/helm/stellaops/values-prod.yaml
+++ b/devops/helm/stellaops/values-prod.yaml
@@ -75,9 +75,8 @@ configMaps:
data:
notify.yaml: |
storage:
- driver: mongo
- connectionString: "mongodb://stellaops-mongo:27017"
- database: "stellaops_notify_prod"
+ driver: postgres
+ connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
commandTimeoutSeconds: 45
authority:
@@ -124,6 +123,9 @@ services:
port: 8440
env:
STELLAOPS_AUTHORITY__ISSUER: "https://authority.prod.stella-ops.org"
+ STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
+ STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
+ STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
envFrom:
@@ -136,6 +138,9 @@ services:
env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "https://licensing.prod.stella-ops.org/introspect"
+ SIGNER__STORAGE__DRIVER: "postgres"
+ SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
+ SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
envFrom:
- secretRef:
name: stellaops-prod-core
@@ -145,6 +150,9 @@ services:
port: 8442
env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
+ ATTESTOR__STORAGE__DRIVER: "postgres"
+ ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
+ ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
envFrom:
- secretRef:
name: stellaops-prod-core
@@ -153,7 +161,10 @@ services:
service:
port: 8445
env:
- CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
+ CONCELIER__STORAGE__DRIVER: "postgres"
+ CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
+ CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
+ CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
envFrom:
- secretRef:
@@ -169,15 +180,17 @@ services:
service:
port: 8444
env:
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "true"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -197,15 +210,17 @@ services:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
replicas: 3
env:
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "true"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -222,6 +237,8 @@ services:
port: 8446
env:
DOTNET_ENVIRONMENT: Production
+ NOTIFY__QUEUE__DRIVER: "valkey"
+ NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
envFrom:
- secretRef:
name: stellaops-prod-notify
@@ -234,6 +251,8 @@ services:
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
env:
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
+ EXCITITOR__STORAGE__DRIVER: "postgres"
+ EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
envFrom:
- secretRef:
name: stellaops-prod-core
@@ -283,42 +302,37 @@ services:
port: 8443
env:
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
- mongo:
+ # Infrastructure services
+ postgres:
class: infrastructure
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
service:
- port: 27017
- command:
- - mongod
- - --bind_ip_all
- envFrom:
- - secretRef:
- name: stellaops-prod-mongo
+ port: 5432
+ env:
+ POSTGRES_USER: stellaops
+ POSTGRES_PASSWORD: stellaops
+ POSTGRES_DB: stellaops
volumeMounts:
- - name: mongo-data
- mountPath: /data/db
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
volumeClaims:
- - name: mongo-data
- claimName: stellaops-mongo-data
- minio:
+ - name: postgres-data
+ claimName: stellaops-postgres-data
+ valkey:
class: infrastructure
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ image: docker.io/valkey/valkey:9.0.1-alpine
service:
- port: 9000
+ port: 6379
command:
- - server
- - /data
- - --console-address
- - :9001
- envFrom:
- - secretRef:
- name: stellaops-prod-minio
+ - valkey-server
+ - --appendonly
+ - "yes"
volumeMounts:
- - name: minio-data
+ - name: valkey-data
mountPath: /data
volumeClaims:
- - name: minio-data
- claimName: stellaops-minio-data
+ - name: valkey-data
+ claimName: stellaops-valkey-data
rustfs:
class: infrastructure
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
diff --git a/devops/helm/stellaops/values-stage.yaml b/devops/helm/stellaops/values-stage.yaml
index e4604d5fc..385084de9 100644
--- a/devops/helm/stellaops/values-stage.yaml
+++ b/devops/helm/stellaops/values-stage.yaml
@@ -21,9 +21,8 @@ configMaps:
data:
notify.yaml: |
storage:
- driver: mongo
- connectionString: "mongodb://notify-mongo.stage.svc.cluster.local:27017"
- database: "stellaops_notify_stage"
+ driver: postgres
+ connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
commandTimeoutSeconds: 45
authority:
@@ -70,7 +69,9 @@ services:
port: 8440
env:
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
- STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
+ STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
+ STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
+ STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
signer:
@@ -80,23 +81,27 @@ services:
env:
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
SIGNER__POE__INTROSPECTURL: "https://licensing.stage.stella-ops.internal/introspect"
- SIGNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
+ SIGNER__STORAGE__DRIVER: "postgres"
+ SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
+ SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
service:
port: 8442
env:
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
- ATTESTOR__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
+ ATTESTOR__STORAGE__DRIVER: "postgres"
+ ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
+ ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
concelier:
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
service:
port: 8445
env:
- CONCELIER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
- CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-minio:9000"
- CONCELIER__STORAGE__S3__ACCESSKEYID: "stellaops-stage"
- CONCELIER__STORAGE__S3__SECRETACCESSKEY: "stage-minio-secret"
+ CONCELIER__STORAGE__DRIVER: "postgres"
+ CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
+ CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
+ CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
volumeMounts:
- name: concelier-jobs
@@ -109,16 +114,17 @@ services:
service:
port: 8444
env:
- SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "false"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -135,16 +141,17 @@ services:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
replicas: 2
env:
- SCANNER__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
+ SCANNER__STORAGE__DRIVER: "postgres"
+ SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
+ SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
- SCANNER__QUEUE__BROKER: "nats://stellaops-nats:4222"
+ SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
SCANNER__EVENTS__ENABLED: "false"
- # Valkey (Redis-compatible) cache driver; keep "redis" for protocol compatibility.
- SCANNER__EVENTS__DRIVER: "redis"
- SCANNER__EVENTS__DSN: ""
+ SCANNER__EVENTS__DRIVER: "valkey"
+ SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
SCANNER__EVENTS__STREAM: "stella.events"
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
@@ -158,6 +165,8 @@ services:
port: 8446
env:
DOTNET_ENVIRONMENT: Production
+ NOTIFY__QUEUE__DRIVER: "valkey"
+ NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
configMounts:
- name: notify-config
mountPath: /app/etc/notify.yaml
@@ -167,49 +176,46 @@ services:
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
env:
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
- EXCITITOR__STORAGE__MONGO__CONNECTIONSTRING: "mongodb://stellaops-stage:stellaops-stage@stellaops-mongo:27017"
+ EXCITITOR__STORAGE__DRIVER: "postgres"
+ EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
web-ui:
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
service:
port: 8443
env:
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
- mongo:
+
+ # Infrastructure services
+ postgres:
class: infrastructure
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
service:
- port: 27017
- command:
- - mongod
- - --bind_ip_all
+ port: 5432
env:
- MONGO_INITDB_ROOT_USERNAME: stellaops-stage
- MONGO_INITDB_ROOT_PASSWORD: stellaops-stage
+ POSTGRES_USER: stellaops
+ POSTGRES_PASSWORD: stellaops
+ POSTGRES_DB: stellaops
volumeMounts:
- - name: mongo-data
- mountPath: /data/db
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
volumeClaims:
- - name: mongo-data
- claimName: stellaops-mongo-data
- minio:
+ - name: postgres-data
+ claimName: stellaops-postgres-data
+ valkey:
class: infrastructure
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ image: docker.io/valkey/valkey:9.0.1-alpine
service:
- port: 9000
+ port: 6379
command:
- - server
- - /data
- - --console-address
- - :9001
- env:
- MINIO_ROOT_USER: stellaops-stage
- MINIO_ROOT_PASSWORD: stage-minio-secret
+ - valkey-server
+ - --appendonly
+ - "yes"
volumeMounts:
- - name: minio-data
+ - name: valkey-data
mountPath: /data
volumeClaims:
- - name: minio-data
- claimName: stellaops-minio-data
+ - name: valkey-data
+ claimName: stellaops-valkey-data
rustfs:
class: infrastructure
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
@@ -230,19 +236,3 @@ services:
volumeClaims:
- name: rustfs-data
claimName: stellaops-rustfs-data
- nats:
- class: infrastructure
- image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
- service:
- port: 4222
- command:
- - -js
- - -sd
- - /data
- volumeMounts:
- - name: nats-data
- mountPath: /data
- volumeClaims:
- - name: nats-data
- claimName: stellaops-nats-data
-
diff --git a/devops/helm/stellaops/values.yaml b/devops/helm/stellaops/values.yaml
index 8e37d649a..e76b39311 100644
--- a/devops/helm/stellaops/values.yaml
+++ b/devops/helm/stellaops/values.yaml
@@ -171,13 +171,10 @@ configMaps:
tenantHeader: X-StellaOps-Tenant
seedCsafPublishers: true
csafSeedPath: data/csaf-publishers.json
- Mongo:
- connectionString: mongodb://mongo:27017
- database: issuer-directory
- issuersCollection: issuers
- issuerKeysCollection: issuer_keys
- issuerTrustCollection: issuer_trust_overrides
- auditCollection: issuer_audit
+ Storage:
+ Driver: postgres
+ Postgres:
+ ConnectionString: Host=postgres;Port=5432;Database=issuer_directory;Username=stellaops;Password=stellaops
policy-engine-activation:
data:
@@ -224,10 +221,10 @@ services:
- dotnet
- StellaOps.Scheduler.Worker.Host.dll
env:
- SCHEDULER__QUEUE__KIND: Nats
- SCHEDULER__QUEUE__NATS__URL: nats://nats:4222
- SCHEDULER__STORAGE__CONNECTIONSTRING: mongodb://scheduler-mongo:27017
- SCHEDULER__STORAGE__DATABASE: stellaops_scheduler
+ SCHEDULER__QUEUE__KIND: Valkey
+ SCHEDULER__QUEUE__VALKEY__URL: valkey:6379
+ SCHEDULER__STORAGE__DRIVER: postgres
+ SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: Host=postgres;Port=5432;Database=scheduler;Username=stellaops;Password=stellaops
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: http://scanner-web:8444
advisory-ai-web:
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.10.0-edge
diff --git a/devops/licensing/AGENTS.md b/devops/licensing/AGENTS.md
deleted file mode 100644
index 9df87e7e5..000000000
--- a/devops/licensing/AGENTS.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Licensing & Registry Access — Agent Charter
-
-## Mission
-Implement licensing token service and registry access workflows described in `docs/modules/devops/ARCHITECTURE.md`.
-
-## Required Reading
-- `docs/modules/platform/architecture-overview.md`
-- `docs/modules/airgap/airgap-mode.md`
-
-## Working Agreement
-- 1. Update task status to `DOING`/`DONE` inside the corresponding `docs/implplan/SPRINT_*.md` entry when you start or finish work.
-- 2. Review this charter and the Required Reading documents before coding; confirm prerequisites are met.
-- 3. Keep changes deterministic (stable ordering, timestamps, hashes) and align with offline/air-gap expectations.
-- 4. Coordinate doc updates, tests, and cross-guild communication whenever contracts or workflows change.
-- 5. Revert to `TODO` if you pause the task without shipping changes; leave notes in commit/PR descriptions for context.
diff --git a/devops/licensing/TASKS.completed.md b/devops/licensing/TASKS.completed.md
deleted file mode 100644
index dc1724220..000000000
--- a/devops/licensing/TASKS.completed.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Completed Tasks
-
-| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
-|----|--------|----------|------------|-------------|---------------|
-| DEVOPS-LIC-14-004 | DONE (2025-10-26) | Licensing Guild | AUTH-MTLS-11-002 | Implement registry token service tied to Authority (DPoP/mTLS), plan gating, revocation handling, and monitoring per architecture. | Token service issues scoped tokens, revocation tested, monitoring dashboards in place, docs updated. |
diff --git a/devops/manifests/binary-plugins.manifest.json b/devops/manifests/binary-plugins.manifest.json
deleted file mode 100644
index 2c8c98d0c..000000000
--- a/devops/manifests/binary-plugins.manifest.json
+++ /dev/null
@@ -1,114 +0,0 @@
-{
- "generated_utc": "2025-11-18T21:41:23.225667Z",
- "summary": "Pinned binaries (non-NuGet) tracked for integrity; relocate new artefacts here or under offline/feeds.",
- "entries": [
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Deno.Tests/StellaOps.Scanner.Analyzers.Lang.Deno.Tests.dll",
- "sha256": "347e600c14671db7015aa3d08b449a7e7bbd9dcfb3b1d4e31cd5a44d2af7b4c7",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Deno/StellaOps.Scanner.Analyzers.Lang.Deno.dll",
- "sha256": "6fb59d1497c6c222df883405177ee7a03e967570671b4a4e39c1ca41df5ee507",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.dll",
- "sha256": "aceea5db1340463db2038cecb528357532d3d5d0102fc9ce0f13d1f0888f0621",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Go/StellaOps.Scanner.Analyzers.Lang.Go.dll",
- "sha256": "87a0308b4e25f29137d2722bf091628d1753a02414e474f6958c01353d78a95f",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Java.Tests/StellaOps.Scanner.Analyzers.Lang.Java.Tests.dll",
- "sha256": "64279fba6e3dcd6e34290565f3d324ad306bc9e971b2fa191eeafbd70868411b",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Java/StellaOps.Scanner.Analyzers.Lang.Java.dll",
- "sha256": "fb2201b2d1ae60c31d2f2390f37b5a574368952e952f05c41989cbec96746dc5",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Node.Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests.dll",
- "sha256": "95f11346a72b28297c307d71c226b2d7f2dc7b465a85b6ca99e6fc739ff92c73",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Node/StellaOps.Scanner.Analyzers.Lang.Node.dll",
- "sha256": "45d59201b3d52fcb022035b00afca0c27f62993d727f5dbfc3ec120e1f3090ba",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Python/StellaOps.Scanner.Analyzers.Lang.Python.dll",
- "sha256": "e4ccaed15c551f859dbee367849c8c99ca5554a5c10926988c9fe2afe0af07ea",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Ruby.Tests/StellaOps.Scanner.Analyzers.Lang.Ruby.Tests.dll",
- "sha256": "a0b641a18ff55056e16c5f15b3124a7fcfa8f99e2e16166b68df9372a79c37b2",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Ruby/StellaOps.Scanner.Analyzers.Lang.Ruby.dll",
- "sha256": "20624ef44aa797339e73e448dbc82e28e9adfac5262ba4b6c9fddb4e1ed89cbc",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Rust.Benchmarks/StellaOps.Scanner.Analyzers.Lang.Rust.Benchmarks.dll",
- "sha256": "a0df5ffdbb043354adef3b3b1203e151b64a4f1c34e560d2bd182188e5535538",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Rust/StellaOps.Scanner.Analyzers.Lang.Rust.dll",
- "sha256": "af19afd814ede740b547514073640a1ce7cd55d346335761d5393d31b0f64224",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Tests/StellaOps.Scanner.Analyzers.Lang.Tests.dll",
- "sha256": "819e7fa3d30d37d972c630c96828ad121bbef184ca977bc2245f9e9ec9815cc8",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/os/StellaOps.Scanner.Analyzers.OS.Apk/StellaOps.Scanner.Analyzers.OS.Apk.dll",
- "sha256": "760b531182a497e76c1fa987d6bd834aa4b369f815542fa6b8e10452dc7048ff",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/os/StellaOps.Scanner.Analyzers.OS.Dpkg/StellaOps.Scanner.Analyzers.OS.Dpkg.dll",
- "sha256": "8cc75f09efa8c656106ed96ad5ab08a0c388aa4beb56aadf6b07bf6d76c00085",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/analyzers/os/StellaOps.Scanner.Analyzers.OS.Rpm/StellaOps.Scanner.Analyzers.OS.Rpm.dll",
- "sha256": "987593dd273f398f07f38b349eaedd6338c5615e976dad1633323348f7b3e9ac",
- "type": "binary",
- "owner": "plugins"
- },
- {
- "path": "plugins/scanner/buildx/StellaOps.Scanner.Sbomer.BuildXPlugin/StellaOps.Scanner.Sbomer.BuildXPlugin.dll",
- "sha256": "4266013acbf3a0d0a02e2682c7e32335c2c3f9263e71b917bac34dac4f70d476",
- "type": "binary",
- "owner": "plugins"
- }
- ]
-}
\ No newline at end of file
diff --git a/devops/manifests/tetragon/stella-ops-tetragon-agent-daemonset.yaml b/devops/manifests/tetragon/stella-ops-tetragon-agent-daemonset.yaml
deleted file mode 100644
index bf7605277..000000000
--- a/devops/manifests/tetragon/stella-ops-tetragon-agent-daemonset.yaml
+++ /dev/null
@@ -1,246 +0,0 @@
-# Tetragon Agent DaemonSet for Stella Ops
-# Sprint: SPRINT_20260118_019_Infra_tetragon_integration
-# Task: TASK-019-007 - Create Kubernetes deployment extending existing manifests
-#
-# Deploys the Stella Ops Tetragon agent alongside the existing agent framework.
-# Follows existing DaemonSet patterns from devops/helm/
-
-apiVersion: apps/v1
-kind: DaemonSet
-metadata:
- name: stella-ops-tetragon-agent
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
- app.kubernetes.io/component: runtime-instrumentation
- app.kubernetes.io/part-of: stella-ops
-spec:
- selector:
- matchLabels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
- updateStrategy:
- type: RollingUpdate
- rollingUpdate:
- maxUnavailable: 1
- template:
- metadata:
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
- app.kubernetes.io/component: runtime-instrumentation
- annotations:
- prometheus.io/scrape: "true"
- prometheus.io/port: "8080"
- prometheus.io/path: "/metrics"
- spec:
- serviceAccountName: stella-ops-tetragon-agent
- hostPID: true
- hostNetwork: false
- tolerations:
- - key: node-role.kubernetes.io/master
- effect: NoSchedule
- - key: node-role.kubernetes.io/control-plane
- effect: NoSchedule
- containers:
- - name: tetragon-agent
- image: stellaops/tetragon-agent:latest
- imagePullPolicy: IfNotPresent
- securityContext:
- privileged: true
- capabilities:
- add:
- - SYS_ADMIN
- - NET_ADMIN
- - BPF
- - PERFMON
- ports:
- - name: metrics
- containerPort: 8080
- protocol: TCP
- - name: health
- containerPort: 8081
- protocol: TCP
- env:
- - name: NODE_NAME
- valueFrom:
- fieldRef:
- fieldPath: spec.nodeName
- - name: STELLA_API_URL
- valueFrom:
- configMapKeyRef:
- name: stella-ops-tetragon-config
- key: api-url
- - name: STELLA_AGENT_ID
- valueFrom:
- fieldRef:
- fieldPath: metadata.name
- - name: TETRAGON_GRPC_ADDRESS
- value: "localhost:54321"
- - name: LOG_LEVEL
- valueFrom:
- configMapKeyRef:
- name: stella-ops-tetragon-config
- key: log-level
- optional: true
- volumeMounts:
- - name: tetragon-config
- mountPath: /etc/tetragon
- readOnly: true
- - name: agent-certs
- mountPath: /etc/stella-ops/certs
- readOnly: true
- - name: bpf
- mountPath: /sys/fs/bpf
- - name: proc
- mountPath: /host/proc
- readOnly: true
- resources:
- requests:
- cpu: 100m
- memory: 128Mi
- limits:
- cpu: 500m
- memory: 512Mi
- livenessProbe:
- httpGet:
- path: /healthz
- port: 8081
- initialDelaySeconds: 10
- periodSeconds: 30
- readinessProbe:
- httpGet:
- path: /ready
- port: 8081
- initialDelaySeconds: 5
- periodSeconds: 10
- volumes:
- - name: tetragon-config
- configMap:
- name: stella-ops-tetragon-policy
- - name: agent-certs
- secret:
- secretName: stella-ops-agent-certs
- optional: true
- - name: bpf
- hostPath:
- path: /sys/fs/bpf
- type: DirectoryOrCreate
- - name: proc
- hostPath:
- path: /proc
- type: Directory
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
- name: stella-ops-tetragon-agent
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: stella-ops-tetragon-agent
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
-rules:
- # Read pods for container correlation
- - apiGroups: [""]
- resources: ["pods", "namespaces"]
- verbs: ["get", "list", "watch"]
- # Read nodes for host information
- - apiGroups: [""]
- resources: ["nodes"]
- verbs: ["get", "list"]
- # Read Tetragon CRDs
- - apiGroups: ["cilium.io"]
- resources: ["tracingpolicies", "tracingpoliciesnamespaced"]
- verbs: ["get", "list", "watch"]
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: stella-ops-tetragon-agent
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
-subjects:
- - kind: ServiceAccount
- name: stella-ops-tetragon-agent
- namespace: stella-ops
-roleRef:
- kind: ClusterRole
- name: stella-ops-tetragon-agent
- apiGroup: rbac.authorization.k8s.io
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: stella-ops-tetragon-config
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
-data:
- api-url: "http://stella-ops-signals.stella-ops.svc.cluster.local:8080"
- log-level: "info"
- aggregation-window: "60s"
- buffer-size: "10000"
- min-confidence: "0.5"
- # Privacy settings
- redact-arguments: "true"
- symbol-id-only-mode: "false"
- # Allowed namespaces (comma-separated, empty = all)
- allowed-namespaces: "stella-ops-workloads,default"
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: stella-ops-tetragon-policy
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
-data:
- policy.yaml: |
- # Reference the TracingPolicy defined in stella-ops-tracing-policy.yaml
- # This ConfigMap can contain additional local policy configurations
- policyRef: stella-ops-runtime-capture
- enableStackTraces: true
- stackTraceSize: 16
- filterNamespaces:
- - stella-ops-workloads
----
-apiVersion: v1
-kind: Service
-metadata:
- name: stella-ops-tetragon-agent
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
-spec:
- type: ClusterIP
- clusterIP: None # Headless for DaemonSet
- ports:
- - name: metrics
- port: 8080
- targetPort: metrics
- - name: health
- port: 8081
- targetPort: health
- selector:
- app.kubernetes.io/name: stella-ops-tetragon-agent
----
-# ServiceMonitor for Prometheus Operator (optional)
-apiVersion: monitoring.coreos.com/v1
-kind: ServiceMonitor
-metadata:
- name: stella-ops-tetragon-agent
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
-spec:
- selector:
- matchLabels:
- app.kubernetes.io/name: stella-ops-tetragon-agent
- endpoints:
- - port: metrics
- interval: 30s
- path: /metrics
diff --git a/devops/manifests/tetragon/stella-ops-tracing-policy.yaml b/devops/manifests/tetragon/stella-ops-tracing-policy.yaml
deleted file mode 100644
index d8b27c3ae..000000000
--- a/devops/manifests/tetragon/stella-ops-tracing-policy.yaml
+++ /dev/null
@@ -1,125 +0,0 @@
-# Tetragon TracingPolicy for Stella Ops Runtime Instrumentation
-# Sprint: SPRINT_20260118_019_Infra_tetragon_integration
-# Task: TASK-019-001 - Define Tetragon TracingPolicy for stack capture
-#
-# This policy captures process execution, syscalls, and stack traces for
-# runtime reachability validation. Integrates with existing Signals infrastructure.
-
-apiVersion: cilium.io/v1alpha1
-kind: TracingPolicy
-metadata:
- name: stella-ops-runtime-capture
- namespace: stella-ops
- labels:
- app.kubernetes.io/name: stella-ops
- app.kubernetes.io/component: runtime-instrumentation
-spec:
- # Process execution events
- kprobes:
- - call: "sys_execve"
- syscall: true
- return: false
- args:
- - index: 0
- type: "string" # filename
- - index: 1
- type: "string" # argv[0]
- selectors:
- - matchNamespaces:
- - namespace: stella-ops-workloads
- operator: In
- matchLabels:
- - key: "stella-ops.io/instrumented"
- operator: Exists
- returnArgAction: Post
-
- # Security-relevant syscalls for reachability validation
- - call: "sys_openat"
- syscall: true
- args:
- - index: 0
- type: "int" # dirfd
- - index: 1
- type: "string" # pathname
- - index: 2
- type: "int" # flags
- selectors:
- - matchNamespaces:
- - namespace: stella-ops-workloads
- operator: In
- - matchArgs:
- - index: 1
- operator: "Prefix"
- values:
- - "/etc/"
- - "/proc/"
- - "/sys/"
- returnArg:
- index: 0
- type: "int"
-
- - call: "sys_connect"
- syscall: true
- args:
- - index: 0
- type: "int" # sockfd
- - index: 1
- type: "sock" # addr struct
- selectors:
- - matchNamespaces:
- - namespace: stella-ops-workloads
- operator: In
- returnArg:
- index: 0
- type: "int"
-
- # Tracepoints for additional coverage
- tracepoints:
- - subsystem: "sched"
- event: "sched_process_exec"
- args:
- - index: 0
- type: "string" # filename
- selectors:
- - matchNamespaces:
- - namespace: stella-ops-workloads
- operator: In
-
- # Stack trace configuration
- options:
- # Enable kernel + userspace stack traces
- stackTraces: true
- # Capture both kernel and user stacks
- stackTraceSize: 16
- # Symbol resolution for userspace
- symbols: true
-
----
-# Companion TracingPolicy for library loading
-apiVersion: cilium.io/v1alpha1
-kind: TracingPolicy
-metadata:
- name: stella-ops-library-capture
- namespace: stella-ops
-spec:
- # Capture dynamic library loading
- uprobes:
- - path: "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
- symbols:
- - "_dl_map_object"
- args:
- - index: 0
- type: "string" # library name
- selectors:
- - matchNamespaces:
- - namespace: stella-ops-workloads
- operator: In
-
- # Alternative for musl-based containers
- - path: "/lib/ld-musl-x86_64.so.1"
- symbols:
- - "__dls3"
- selectors:
- - matchNamespaces:
- - namespace: stella-ops-workloads
- operator: In
diff --git a/devops/mock-release/README.md b/devops/mock-release/README.md
deleted file mode 100644
index 618e4f551..000000000
--- a/devops/mock-release/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Mock Dev Release Pipeline
-
-Purpose: provide a minimal CI artifact so deploy tasks can progress with placeholder digests until real releases land.
-
-What it does:
-- Packages `deploy/releases/2025.09-mock-dev.yaml` and `deploy/downloads/manifest.json` into `out/mock-release/mock-dev-release.tgz`.
-- Uploads the tarball as a CI artifact (`mock-dev-release`) for downstream consumers (deploy packaging, docs snapshots, local testing).
-
-How to run locally:
-```bash
-mkdir -p out/mock-release
-cp deploy/releases/2025.09-mock-dev.yaml out/mock-release/
-cp deploy/downloads/manifest.json out/mock-release/
-tar -czf out/mock-release/mock-dev-release.tgz -C out/mock-release .
-```
-
-CI entrypoint:
-- Workflow: `.gitea/workflows/mock-dev-release.yml`
-- Triggers: push to mock manifest/downloads files or manual `workflow_dispatch`.
-
-Notes:
-- Artefacts are **development-only**; replace with real digests as soon as upstream releases publish.
-- Keep the mock manifest and downloads JSON deterministic to avoid artifact churn.***
diff --git a/devops/mock-release/config_check.sh b/devops/mock-release/config_check.sh
deleted file mode 100644
index ba6877d07..000000000
--- a/devops/mock-release/config_check.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-cd "$(dirname "$0")/../../deploy/compose"
-docker compose --env-file env/dev.env.example --env-file env/mock.env.example \
- -f docker-compose.dev.yaml -f docker-compose.mock.yaml config > /tmp/compose-mock-config.yaml
-echo "compose config written to /tmp/compose-mock-config.yaml"
diff --git a/devops/observability/alerting/hlc-alerts.yaml b/devops/observability/alerting/hlc-alerts.yaml
deleted file mode 100644
index d1d50e74c..000000000
--- a/devops/observability/alerting/hlc-alerts.yaml
+++ /dev/null
@@ -1,119 +0,0 @@
-# HLC Queue Alerting Rules
-# Sprint: SPRINT_20260105_002_004_BE_hlc_integration_tests
-# Task: INT-018 - Create alerts for HLC anomalies
-
-groups:
- - name: hlc_alerts
- interval: 1m
- rules:
- # Critical: Chain verification failures indicate tampering or corruption
- - alert: HlcChainVerificationFailure
- expr: increase(scheduler_chain_verification_failures_total[5m]) > 0
- for: 1m
- labels:
- severity: critical
- team: scheduler
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#chain-verification-failure
- annotations:
- summary: "HLC chain verification failure detected"
- description: "Chain verification failure on node {{ $labels.node_id }} for tenant {{ $labels.tenant_id }}. This may indicate data tampering or corruption."
- impact: "Audit trail integrity compromised. Investigation required."
- action: "1. Check scheduler_log table for gaps. 2. Verify no unauthorized changes. 3. Review chain head consistency."
-
- # Critical: Clock skew exceeds tolerance - can cause ordering issues
- - alert: HlcClockSkewExceedsTolerance
- expr: increase(hlc_clock_skew_rejections_total[5m]) > 5
- for: 2m
- labels:
- severity: critical
- team: infrastructure
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#clock-skew
- annotations:
- summary: "HLC clock skew rejections on {{ $labels.node_id }}"
- description: "Node {{ $labels.node_id }} is rejecting HLC updates due to clock skew. {{ $value }} rejections in last 5 minutes."
- impact: "Job ordering may be inconsistent. Distributed consistency at risk."
- action: "1. Check NTP synchronization on affected node. 2. Verify time sources. 3. Consider increasing skew tolerance temporarily."
-
- # Warning: Physical time offset is drifting
- - alert: HlcPhysicalTimeOffset
- expr: abs(hlc_physical_time_offset_seconds) > 0.5
- for: 5m
- labels:
- severity: warning
- team: infrastructure
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#time-offset
- annotations:
- summary: "HLC physical time offset on {{ $labels.node_id }}"
- description: "HLC physical time is {{ $value }}s offset from wall clock on {{ $labels.node_id }}."
- impact: "May cause timestamp ordering anomalies in logs and diagnostics."
- action: "Monitor NTP status and consider clock synchronization."
-
- # Warning: High merge conflict rate in air-gap sync
- - alert: HlcMergeConflictRateHigh
- expr: increase(airgap_merge_conflicts_total[1h]) > 100
- for: 10m
- labels:
- severity: warning
- team: scheduler
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#merge-conflicts
- annotations:
- summary: "High HLC merge conflict rate during air-gap sync"
- description: "{{ $value }} merge conflicts detected in the last hour for conflict type {{ $labels.conflict_type }}."
- impact: "Air-gap sync may be producing unexpected results or dropping jobs."
- action: "1. Review conflict resolution logs. 2. Check for duplicate job submissions. 3. Verify offline node clocks."
-
- # Warning: Air-gap sync duration increasing
- - alert: HlcSyncDurationHigh
- expr: histogram_quantile(0.95, sum(rate(airgap_sync_duration_seconds_bucket[15m])) by (le)) > 30
- for: 10m
- labels:
- severity: warning
- team: scheduler
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#slow-sync
- annotations:
- summary: "Air-gap sync duration is high"
- description: "95th percentile sync duration is {{ $value }}s, exceeding 30s threshold."
- impact: "Air-gap import operations are slow, may delay job processing."
- action: "1. Check bundle sizes. 2. Verify database performance. 3. Consider chunking large bundles."
-
- # Info: HLC enqueue rate is zero (may be expected in some deployments)
- - alert: HlcEnqueueRateZero
- expr: sum(rate(scheduler_hlc_enqueues_total[10m])) == 0
- for: 30m
- labels:
- severity: info
- team: scheduler
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#no-enqueues
- annotations:
- summary: "No HLC enqueues in last 30 minutes"
- description: "No jobs have been enqueued with HLC timestamps in the last 30 minutes."
- impact: "May be expected if no jobs are scheduled, or may indicate HLC ordering is disabled."
- action: "Verify EnableHlcOrdering configuration if HLC ordering is expected."
-
- # Warning: Batch snapshot creation failing
- - alert: HlcBatchSnapshotFailures
- expr: increase(scheduler_batch_snapshot_failures_total[5m]) > 0
- for: 2m
- labels:
- severity: warning
- team: scheduler
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#batch-snapshot-failure
- annotations:
- summary: "Batch snapshot creation failures"
- description: "{{ $value }} batch snapshot creation failures in the last 5 minutes."
- impact: "DSSE-signed batch proofs may be missing for affected time ranges."
- action: "1. Check signing key availability. 2. Verify database connectivity. 3. Review batch size limits."
-
- # Critical: Multiple nodes with same node ID (configuration error)
- - alert: HlcDuplicateNodeId
- expr: count by (node_id) (group by (node_id, instance) (hlc_ticks_total)) > 1
- for: 5m
- labels:
- severity: critical
- team: scheduler
- runbook: https://docs.stellaops.internal/operations/runbooks/hlc-troubleshooting#duplicate-node-id
- annotations:
- summary: "Duplicate HLC node ID detected"
- description: "Multiple instances are using node_id={{ $labels.node_id }}. This will cause ordering conflicts."
- impact: "Critical: Job ordering and chain integrity will be compromised."
- action: "Immediately reconfigure affected instances with unique node IDs."
diff --git a/devops/observability/grafana/dashboards/unknowns-queue-dashboard.json b/devops/observability/grafana/dashboards/unknowns-queue-dashboard.json
deleted file mode 100644
index 53d1bace7..000000000
--- a/devops/observability/grafana/dashboards/unknowns-queue-dashboard.json
+++ /dev/null
@@ -1,361 +0,0 @@
-{
- "__inputs": [],
- "annotations": {
- "list": []
- },
- "description": "Unknowns Queue monitoring dashboard - Sprint SPRINT_20260118_018_Unknowns_queue_enhancement (UQ-007)",
- "editable": true,
- "fiscalYearStartMonth": 0,
- "graphTooltip": 0,
- "id": null,
- "links": [],
- "liveNow": false,
- "panels": [
- {
- "title": "Queue Overview",
- "type": "row",
- "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 },
- "collapsed": false
- },
- {
- "title": "Total Queue Depth",
- "type": "stat",
- "gridPos": { "h": 4, "w": 4, "x": 0, "y": 1 },
- "targets": [
- {
- "expr": "sum(unknowns_queue_depth_hot + unknowns_queue_depth_warm + unknowns_queue_depth_cold)",
- "legendFormat": "Total"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 0, "color": "green" },
- { "value": 50, "color": "yellow" },
- { "value": 100, "color": "red" }
- ]
- }
- }
- }
- },
- {
- "title": "HOT Unknowns",
- "type": "stat",
- "gridPos": { "h": 4, "w": 4, "x": 4, "y": 1 },
- "targets": [
- {
- "expr": "unknowns_queue_depth_hot",
- "legendFormat": "HOT"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "color": { "mode": "thresholds" },
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 0, "color": "green" },
- { "value": 1, "color": "orange" },
- { "value": 5, "color": "red" }
- ]
- }
- }
- }
- },
- {
- "title": "WARM Unknowns",
- "type": "stat",
- "gridPos": { "h": 4, "w": 4, "x": 8, "y": 1 },
- "targets": [
- {
- "expr": "unknowns_queue_depth_warm",
- "legendFormat": "WARM"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "color": { "mode": "thresholds" },
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 0, "color": "green" },
- { "value": 10, "color": "yellow" },
- { "value": 25, "color": "orange" }
- ]
- }
- }
- }
- },
- {
- "title": "COLD Unknowns",
- "type": "stat",
- "gridPos": { "h": 4, "w": 4, "x": 12, "y": 1 },
- "targets": [
- {
- "expr": "unknowns_queue_depth_cold",
- "legendFormat": "COLD"
- }
- ]
- },
- {
- "title": "SLA Compliance",
- "type": "gauge",
- "gridPos": { "h": 4, "w": 4, "x": 16, "y": 1 },
- "targets": [
- {
- "expr": "unknowns_sla_compliance * 100",
- "legendFormat": "Compliance %"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "unit": "percent",
- "min": 0,
- "max": 100,
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 0, "color": "red" },
- { "value": 80, "color": "yellow" },
- { "value": 95, "color": "green" }
- ]
- }
- }
- }
- },
- {
- "title": "Stuck Processing",
- "type": "stat",
- "gridPos": { "h": 4, "w": 4, "x": 20, "y": 1 },
- "targets": [
- {
- "expr": "greyqueue_processing_count",
- "legendFormat": "Processing"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 0, "color": "green" },
- { "value": 5, "color": "yellow" },
- { "value": 10, "color": "red" }
- ]
- }
- }
- }
- },
- {
- "title": "Queue Depth Over Time",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 5 },
- "targets": [
- {
- "expr": "unknowns_queue_depth_hot",
- "legendFormat": "HOT"
- },
- {
- "expr": "unknowns_queue_depth_warm",
- "legendFormat": "WARM"
- },
- {
- "expr": "unknowns_queue_depth_cold",
- "legendFormat": "COLD"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "custom": {
- "lineWidth": 2,
- "fillOpacity": 20
- }
- },
- "overrides": [
- { "matcher": { "id": "byName", "options": "HOT" }, "properties": [{ "id": "color", "value": { "fixedColor": "red" } }] },
- { "matcher": { "id": "byName", "options": "WARM" }, "properties": [{ "id": "color", "value": { "fixedColor": "orange" } }] },
- { "matcher": { "id": "byName", "options": "COLD" }, "properties": [{ "id": "color", "value": { "fixedColor": "blue" } }] }
- ]
- }
- },
- {
- "title": "SLA Compliance Over Time",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 5 },
- "targets": [
- {
- "expr": "unknowns_sla_compliance * 100",
- "legendFormat": "SLA Compliance %"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "unit": "percent",
- "min": 0,
- "max": 100,
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 80, "color": "yellow" },
- { "value": 95, "color": "green" }
- ]
- }
- }
- }
- },
- {
- "title": "Operations",
- "type": "row",
- "gridPos": { "h": 1, "w": 24, "x": 0, "y": 13 },
- "collapsed": false
- },
- {
- "title": "State Transitions (Rate)",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 14 },
- "targets": [
- {
- "expr": "rate(unknowns_state_transitions_total[5m])",
- "legendFormat": "{{from_state}} → {{to_state}}"
- }
- ]
- },
- {
- "title": "Processing Time (p95)",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 14 },
- "targets": [
- {
- "expr": "histogram_quantile(0.95, rate(unknowns_processing_time_seconds_bucket[5m]))",
- "legendFormat": "p95 Processing Time"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "unit": "s"
- }
- }
- },
- {
- "title": "Escalations & Failures",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 22 },
- "targets": [
- {
- "expr": "rate(unknowns_escalated_total[1h])",
- "legendFormat": "Escalations"
- },
- {
- "expr": "rate(unknowns_demoted_total[1h])",
- "legendFormat": "Demotions"
- },
- {
- "expr": "rate(unknowns_expired_total[1h])",
- "legendFormat": "Expired"
- },
- {
- "expr": "rate(greyqueue_watchdog_failed_total[1h])",
- "legendFormat": "Failed"
- }
- ]
- },
- {
- "title": "Resolution Time by Band",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 22 },
- "targets": [
- {
- "expr": "histogram_quantile(0.50, rate(unknowns_resolution_time_hours_bucket{band=\"hot\"}[1h]))",
- "legendFormat": "HOT (p50)"
- },
- {
- "expr": "histogram_quantile(0.50, rate(unknowns_resolution_time_hours_bucket{band=\"warm\"}[1h]))",
- "legendFormat": "WARM (p50)"
- },
- {
- "expr": "histogram_quantile(0.50, rate(unknowns_resolution_time_hours_bucket{band=\"cold\"}[1h]))",
- "legendFormat": "COLD (p50)"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "unit": "h"
- }
- }
- },
- {
- "title": "Watchdog Metrics",
- "type": "row",
- "gridPos": { "h": 1, "w": 24, "x": 0, "y": 30 },
- "collapsed": false
- },
- {
- "title": "Stuck & Timeout Events",
- "type": "timeseries",
- "gridPos": { "h": 6, "w": 12, "x": 0, "y": 31 },
- "targets": [
- {
- "expr": "rate(greyqueue_stuck_total[1h]) * 3600",
- "legendFormat": "Stuck (per hour)"
- },
- {
- "expr": "rate(greyqueue_timeout_total[1h]) * 3600",
- "legendFormat": "Timeouts (per hour)"
- },
- {
- "expr": "rate(greyqueue_watchdog_retry_total[1h]) * 3600",
- "legendFormat": "Forced Retries (per hour)"
- }
- ]
- },
- {
- "title": "Currently Processing",
- "type": "stat",
- "gridPos": { "h": 6, "w": 6, "x": 12, "y": 31 },
- "targets": [
- {
- "expr": "greyqueue_processing_count",
- "legendFormat": "In Processing"
- }
- ]
- },
- {
- "title": "SLA Breaches Today",
- "type": "stat",
- "gridPos": { "h": 6, "w": 6, "x": 18, "y": 31 },
- "targets": [
- {
- "expr": "increase(unknowns_sla_breach_total[24h])",
- "legendFormat": "Breaches (24h)"
- }
- ],
- "fieldConfig": {
- "defaults": {
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "value": 0, "color": "green" },
- { "value": 1, "color": "red" }
- ]
- }
- }
- }
- }
- ],
- "refresh": "30s",
- "schemaVersion": 38,
- "style": "dark",
- "tags": ["unknowns", "security", "sla"],
- "templating": {
- "list": []
- },
- "time": {
- "from": "now-6h",
- "to": "now"
- },
- "title": "Unknowns Queue Dashboard",
- "uid": "unknowns-queue-dashboard",
- "version": 1
-}
diff --git a/devops/observability/grafana/hlc-queue-metrics.json b/devops/observability/grafana/hlc-queue-metrics.json
deleted file mode 100644
index 91ff4608d..000000000
--- a/devops/observability/grafana/hlc-queue-metrics.json
+++ /dev/null
@@ -1,290 +0,0 @@
-{
- "dashboard": {
- "id": null,
- "uid": "stellaops-hlc-metrics",
- "title": "StellaOps HLC Queue Metrics",
- "description": "Hybrid Logical Clock ordering metrics for the Scheduler queue",
- "tags": ["stellaops", "hlc", "scheduler", "audit"],
- "timezone": "utc",
- "schemaVersion": 39,
- "version": 1,
- "refresh": "30s",
- "time": {
- "from": "now-1h",
- "to": "now"
- },
- "panels": [
- {
- "id": 1,
- "title": "HLC Tick Rate",
- "description": "Rate of HLC tick operations per second",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
- "fieldConfig": {
- "defaults": {
- "unit": "ops",
- "custom": { "drawStyle": "line", "lineInterpolation": "smooth" }
- }
- },
- "targets": [
- {
- "expr": "rate(hlc_ticks_total[1m])",
- "legendFormat": "{{node_id}}",
- "refId": "A"
- }
- ]
- },
- {
- "id": 2,
- "title": "Clock Skew Rejections",
- "description": "HLC rejections due to clock skew exceeding tolerance",
- "type": "stat",
- "gridPos": { "h": 4, "w": 6, "x": 12, "y": 0 },
- "fieldConfig": {
- "defaults": {
- "unit": "short",
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "color": "green", "value": null },
- { "color": "yellow", "value": 1 },
- { "color": "red", "value": 10 }
- ]
- }
- }
- },
- "targets": [
- {
- "expr": "sum(increase(hlc_clock_skew_rejections_total[1h]))",
- "refId": "A"
- }
- ]
- },
- {
- "id": 3,
- "title": "Physical Time Offset",
- "description": "Difference between HLC physical time and wall clock",
- "type": "gauge",
- "gridPos": { "h": 4, "w": 6, "x": 18, "y": 0 },
- "fieldConfig": {
- "defaults": {
- "unit": "ms",
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "color": "green", "value": null },
- { "color": "yellow", "value": 100 },
- { "color": "red", "value": 1000 }
- ]
- },
- "max": 5000
- }
- },
- "targets": [
- {
- "expr": "max(hlc_physical_time_offset_seconds) * 1000",
- "refId": "A"
- }
- ]
- },
- {
- "id": 4,
- "title": "Scheduler HLC Enqueues",
- "description": "Rate of jobs enqueued with HLC timestamps",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 },
- "fieldConfig": {
- "defaults": {
- "unit": "ops",
- "custom": { "drawStyle": "bars", "fillOpacity": 50 }
- }
- },
- "targets": [
- {
- "expr": "rate(scheduler_hlc_enqueues_total[5m])",
- "legendFormat": "{{tenant_id}}",
- "refId": "A"
- }
- ]
- },
- {
- "id": 5,
- "title": "Chain Verifications",
- "description": "Chain verification operations by result",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
- "fieldConfig": {
- "defaults": {
- "unit": "ops"
- },
- "overrides": [
- {
- "matcher": { "id": "byName", "options": "valid" },
- "properties": [{ "id": "color", "value": { "fixedColor": "green", "mode": "fixed" } }]
- },
- {
- "matcher": { "id": "byName", "options": "invalid" },
- "properties": [{ "id": "color", "value": { "fixedColor": "red", "mode": "fixed" } }]
- }
- ]
- },
- "targets": [
- {
- "expr": "rate(scheduler_chain_verifications_total[5m])",
- "legendFormat": "{{result}}",
- "refId": "A"
- }
- ]
- },
- {
- "id": 6,
- "title": "Verification Failures",
- "description": "Chain verification failures - indicates tampering or corruption",
- "type": "stat",
- "gridPos": { "h": 4, "w": 6, "x": 12, "y": 8 },
- "fieldConfig": {
- "defaults": {
- "unit": "short",
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "color": "green", "value": null },
- { "color": "red", "value": 1 }
- ]
- }
- }
- },
- "targets": [
- {
- "expr": "sum(increase(scheduler_chain_verification_failures_total[1h]))",
- "refId": "A"
- }
- ]
- },
- {
- "id": 7,
- "title": "Batch Snapshots",
- "description": "Batch snapshot creation rate",
- "type": "stat",
- "gridPos": { "h": 4, "w": 6, "x": 18, "y": 8 },
- "fieldConfig": {
- "defaults": {
- "unit": "short"
- }
- },
- "targets": [
- {
- "expr": "sum(increase(scheduler_batch_snapshots_total[1h]))",
- "refId": "A"
- }
- ]
- },
- {
- "id": 8,
- "title": "Air-Gap Bundle Exports",
- "description": "Rate of air-gap bundles exported",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 8, "x": 0, "y": 16 },
- "fieldConfig": {
- "defaults": {
- "unit": "ops"
- }
- },
- "targets": [
- {
- "expr": "rate(airgap_bundles_exported_total[5m])",
- "legendFormat": "{{node_id}}",
- "refId": "A"
- }
- ]
- },
- {
- "id": 9,
- "title": "Air-Gap Bundle Imports",
- "description": "Rate of air-gap bundles imported",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 8, "x": 8, "y": 16 },
- "fieldConfig": {
- "defaults": {
- "unit": "ops"
- }
- },
- "targets": [
- {
- "expr": "rate(airgap_bundles_imported_total[5m])",
- "legendFormat": "imported",
- "refId": "A"
- }
- ]
- },
- {
- "id": 10,
- "title": "Air-Gap Merge Conflicts",
- "description": "Merge conflicts by type during air-gap sync",
- "type": "stat",
- "gridPos": { "h": 4, "w": 8, "x": 16, "y": 16 },
- "fieldConfig": {
- "defaults": {
- "unit": "short",
- "thresholds": {
- "mode": "absolute",
- "steps": [
- { "color": "green", "value": null },
- { "color": "yellow", "value": 1 },
- { "color": "red", "value": 10 }
- ]
- }
- }
- },
- "targets": [
- {
- "expr": "sum by (conflict_type) (increase(airgap_merge_conflicts_total[1h]))",
- "legendFormat": "{{conflict_type}}",
- "refId": "A"
- }
- ]
- },
- {
- "id": 11,
- "title": "Sync Duration",
- "description": "Air-gap sync operation duration percentiles",
- "type": "timeseries",
- "gridPos": { "h": 8, "w": 8, "x": 16, "y": 20 },
- "fieldConfig": {
- "defaults": {
- "unit": "s"
- }
- },
- "targets": [
- {
- "expr": "histogram_quantile(0.50, sum(rate(airgap_sync_duration_seconds_bucket[5m])) by (le))",
- "legendFormat": "p50",
- "refId": "A"
- },
- {
- "expr": "histogram_quantile(0.95, sum(rate(airgap_sync_duration_seconds_bucket[5m])) by (le))",
- "legendFormat": "p95",
- "refId": "B"
- },
- {
- "expr": "histogram_quantile(0.99, sum(rate(airgap_sync_duration_seconds_bucket[5m])) by (le))",
- "legendFormat": "p99",
- "refId": "C"
- }
- ]
- }
- ],
- "annotations": {
- "list": [
- {
- "name": "Deployments",
- "datasource": "-- Grafana --",
- "enable": true,
- "iconColor": "blue"
- }
- ]
- }
- },
- "folderId": 0,
- "overwrite": true
-}
diff --git a/devops/observability/grafana/policy-pipeline.json b/devops/observability/grafana/policy-pipeline.json
deleted file mode 100644
index d29e3e2ad..000000000
--- a/devops/observability/grafana/policy-pipeline.json
+++ /dev/null
@@ -1,78 +0,0 @@
-{
- "schemaVersion": 39,
- "title": "Policy Pipeline",
- "panels": [
- {
- "type": "stat",
- "title": "Compile p99 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 2}},
- "targets": [
- {"expr": "histogram_quantile(0.99, sum(rate(policy_compile_duration_seconds_bucket[5m])) by (le))"}
- ]
- },
- {
- "type": "timeseries",
- "title": "Compile Duration (p95/p50)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 2}},
- "targets": [
- {"expr": "histogram_quantile(0.95, sum(rate(policy_compile_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p95"},
- {"expr": "histogram_quantile(0.50, sum(rate(policy_compile_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p50"}
- ]
- },
- {
- "type": "stat",
- "title": "Simulation Queue Depth",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "none"}},
- "targets": [{"expr": "sum(policy_simulation_queue_depth)"}]
- },
- {
- "type": "timeseries",
- "title": "Queue Depth by Stage",
- "datasource": "Prometheus",
- "targets": [{"expr": "policy_simulation_queue_depth", "legendFormat": "{{stage}}"}],
- "fieldConfig": {"defaults": {"unit": "none"}}
- },
- {
- "type": "stat",
- "title": "Approval p95 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 1}},
- "targets": [
- {"expr": "histogram_quantile(0.95, sum(rate(policy_approval_latency_seconds_bucket[5m])) by (le))"}
- ]
- },
- {
- "type": "timeseries",
- "title": "Approval Latency",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 1}},
- "targets": [
- {"expr": "histogram_quantile(0.90, sum(rate(policy_approval_latency_seconds_bucket[5m])) by (le))", "legendFormat": "p90"},
- {"expr": "histogram_quantile(0.50, sum(rate(policy_approval_latency_seconds_bucket[5m])) by (le))", "legendFormat": "p50"}
- ]
- },
- {
- "type": "gauge",
- "title": "Promotion Success Rate (30m)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "percent", "min": 0, "max": 100}},
- "options": {"reduceOptions": {"calcs": ["last"]}, "orientation": "horizontal"},
- "targets": [
- {"expr": "100 * clamp_min(rate(policy_promotion_outcomes_total{outcome=\"success\"}[30m]),0) / clamp_min(rate(policy_promotion_outcomes_total[30m]),1)"}
- ]
- },
- {
- "type": "barchart",
- "title": "Promotion Outcomes",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "1/s"}},
- "options": {"displayMode": "series"},
- "targets": [
- {"expr": "rate(policy_promotion_outcomes_total[5m])", "legendFormat": "{{outcome}}"}
- ]
- }
- ]
-}
diff --git a/devops/observability/grafana/signals-pipeline.json b/devops/observability/grafana/signals-pipeline.json
deleted file mode 100644
index 63e44ffb6..000000000
--- a/devops/observability/grafana/signals-pipeline.json
+++ /dev/null
@@ -1,74 +0,0 @@
-{
- "schemaVersion": 39,
- "title": "Signals Pipeline",
- "panels": [
- {
- "type": "stat",
- "title": "Scoring p95 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 2}},
- "targets": [
- {"expr": "histogram_quantile(0.95, sum(rate(signals_reachability_scoring_duration_seconds_bucket[5m])) by (le))"}
- ]
- },
- {
- "type": "timeseries",
- "title": "Scoring Duration p95/p50",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 2}},
- "targets": [
- {"expr": "histogram_quantile(0.95, sum(rate(signals_reachability_scoring_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p95"},
- {"expr": "histogram_quantile(0.50, sum(rate(signals_reachability_scoring_duration_seconds_bucket[5m])) by (le))", "legendFormat": "p50"}
- ]
- },
- {
- "type": "gauge",
- "title": "Cache Hit Ratio (5m)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "percent", "min": 0, "max": 100}},
- "options": {"reduceOptions": {"calcs": ["last"]}, "orientation": "horizontal"},
- "targets": [
- {"expr": "100 * clamp_min(rate(signals_cache_hits_total[5m]),0) / clamp_min(rate(signals_cache_hits_total[5m]) + rate(signals_cache_misses_total[5m]), 1)"}
- ]
- },
- {
- "type": "timeseries",
- "title": "Cache Hits/Misses",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "1/s"}},
- "targets": [
- {"expr": "rate(signals_cache_hits_total[5m])", "legendFormat": "hits"},
- {"expr": "rate(signals_cache_misses_total[5m])", "legendFormat": "misses"}
- ]
- },
- {
- "type": "stat",
- "title": "Sensors Reporting",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "none"}},
- "targets": [
- {"expr": "count(max_over_time(signals_sensor_last_seen_timestamp_seconds[15m]))"}
- ]
- },
- {
- "type": "timeseries",
- "title": "Sensor Staleness",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s"}},
- "targets": [
- {"expr": "time() - max(signals_sensor_last_seen_timestamp_seconds) by (sensor)", "legendFormat": "{{sensor}}"}
- ]
- },
- {
- "type": "barchart",
- "title": "Ingestion Outcomes",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "1/s"}},
- "options": {"displayMode": "series"},
- "targets": [
- {"expr": "rate(signals_ingestion_total[5m])", "legendFormat": "total"},
- {"expr": "rate(signals_ingestion_failures_total[5m])", "legendFormat": "failures"}
- ]
- }
- ]
-}
diff --git a/devops/observability/grafana/slo-burn.json b/devops/observability/grafana/slo-burn.json
deleted file mode 100644
index 6e35a45b4..000000000
--- a/devops/observability/grafana/slo-burn.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "title": "SLO Burn",
- "time": { "from": "now-24h", "to": "now" },
- "panels": [
- {
- "type": "timeseries",
- "title": "Error rate",
- "targets": [
- { "expr": "rate(service_request_errors_total[5m]) / rate(service_requests_total[5m])", "legendFormat": "5m" },
- { "expr": "rate(service_request_errors_total[1h]) / rate(service_requests_total[1h])", "legendFormat": "1h" }
- ],
- "fieldConfig": {
- "defaults": { "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 0.01 } ] } }
- }
- },
- {
- "type": "stat",
- "title": "Budget used (24h)",
- "targets": [
- { "expr": "(sum_over_time(service_request_errors_total[24h]) / sum_over_time(service_requests_total[24h]))" }
- ]
- }
- ],
- "schemaVersion": 39,
- "version": 1
-}
diff --git a/devops/observability/grafana/triage-ttfs.json b/devops/observability/grafana/triage-ttfs.json
deleted file mode 100644
index cac0044a1..000000000
--- a/devops/observability/grafana/triage-ttfs.json
+++ /dev/null
@@ -1,97 +0,0 @@
-{
- "schemaVersion": 39,
- "title": "Triage TTFS",
- "panels": [
- {
- "type": "stat",
- "title": "TTFS First Evidence p95 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 3}},
- "targets": [
- {"expr": "histogram_quantile(0.95, sum(rate(stellaops_ttfs_first_evidence_seconds_bucket[5m])) by (le))"}
- ]
- },
- {
- "type": "timeseries",
- "title": "TTFS First Evidence p50/p95 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 3}},
- "targets": [
- {"expr": "histogram_quantile(0.50, sum(rate(stellaops_ttfs_first_evidence_seconds_bucket[5m])) by (le))", "legendFormat": "p50"},
- {"expr": "histogram_quantile(0.95, sum(rate(stellaops_ttfs_first_evidence_seconds_bucket[5m])) by (le))", "legendFormat": "p95"}
- ]
- },
- {
- "type": "timeseries",
- "title": "TTFS Skeleton p50/p95 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 3}},
- "targets": [
- {"expr": "histogram_quantile(0.50, sum(rate(stellaops_ttfs_skeleton_seconds_bucket[5m])) by (le))", "legendFormat": "p50"},
- {"expr": "histogram_quantile(0.95, sum(rate(stellaops_ttfs_skeleton_seconds_bucket[5m])) by (le))", "legendFormat": "p95"}
- ]
- },
- {
- "type": "timeseries",
- "title": "TTFS Full Evidence p50/p95 (s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "s", "decimals": 3}},
- "targets": [
- {"expr": "histogram_quantile(0.50, sum(rate(stellaops_ttfs_full_evidence_seconds_bucket[5m])) by (le))", "legendFormat": "p50"},
- {"expr": "histogram_quantile(0.95, sum(rate(stellaops_ttfs_full_evidence_seconds_bucket[5m])) by (le))", "legendFormat": "p95"}
- ]
- },
- {
- "type": "stat",
- "title": "Clicks-to-Closure Median",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "none", "decimals": 1}},
- "targets": [
- {"expr": "histogram_quantile(0.50, sum(rate(stellaops_clicks_to_closure_bucket[5m])) by (le))"}
- ]
- },
- {
- "type": "timeseries",
- "title": "Clicks-to-Closure p50/p95",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "none", "decimals": 1}},
- "targets": [
- {"expr": "histogram_quantile(0.50, sum(rate(stellaops_clicks_to_closure_bucket[5m])) by (le))", "legendFormat": "p50"},
- {"expr": "histogram_quantile(0.95, sum(rate(stellaops_clicks_to_closure_bucket[5m])) by (le))", "legendFormat": "p95"}
- ]
- },
- {
- "type": "stat",
- "title": "Evidence Completeness Avg (%)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "percent", "decimals": 1}},
- "targets": [
- {
- "expr": "100 * (sum(rate(stellaops_evidence_completeness_score_sum[5m])) / clamp_min(sum(rate(stellaops_evidence_completeness_score_count[5m])), 1)) / 4"
- }
- ]
- },
- {
- "type": "timeseries",
- "title": "Evidence Completeness Avg (%)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "percent", "decimals": 1}},
- "targets": [
- {
- "expr": "100 * (sum(rate(stellaops_evidence_completeness_score_sum[5m])) / clamp_min(sum(rate(stellaops_evidence_completeness_score_count[5m])), 1)) / 4",
- "legendFormat": "avg"
- }
- ]
- },
- {
- "type": "barchart",
- "title": "Budget Violations Rate (1/s)",
- "datasource": "Prometheus",
- "fieldConfig": {"defaults": {"unit": "1/s"}},
- "options": {"displayMode": "series"},
- "targets": [
- {"expr": "sum(rate(stellaops_performance_budget_violations_total[5m])) by (phase)", "legendFormat": "{{phase}}"}
- ]
- }
- ]
-}
diff --git a/devops/observability/incident-mode.md b/devops/observability/incident-mode.md
deleted file mode 100644
index 0e60f7408..000000000
--- a/devops/observability/incident-mode.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Incident Mode Automation (DEVOPS-OBS-55-001)
-
-## What it does
-- Auto-enables an *incident* feature flag when SLO burn rate crosses a threshold.
-- Writes deterministic retention overrides (hours) for downstream storage/ingest.
-- Auto-clears after a cooldown once burn is back under the reset threshold.
-- Offline-friendly: no external calls; pure file outputs under `out/incident-mode/`.
-
-## Inputs
-- Burn rate multiple (fast-burn): required.
-- Thresholds/cooldown/retention configurable via CLI flags or env vars.
-- Optional note for audit context.
-
-## Outputs
-- `flag.json` — enabled/disabled + burn rate and note.
-- `retention.json` — retention override hours + applied time.
-- `last_burn.txt`, `cooldown.txt` — trace for automation/testing.
-
-## Usage
-```bash
-# Activate if burn >= 2.5, otherwise decay cooldown; clear after 15 mins <0.4
-scripts/observability/incident-mode.sh \
- --burn-rate 3.2 \
- --threshold 2.5 \
- --reset-threshold 0.4 \
- --cooldown-mins 15 \
- --retention-hours 48 \
- --note "api error burst"
-
-# Later (burn back to normal):
-scripts/observability/incident-mode.sh --burn-rate 0.2 --reset-threshold 0.4 --cooldown-mins 15
-```
-Outputs land in `out/incident-mode/` by default (override with `--state-dir`).
-
-## Integration hooks
-- Prometheus rule should page on SLOBurnRateFast (already in `alerts-slo.yaml`).
-- A small runner (cron/workflow) can feed burn rate into this script from PromQL
- (`scalar(slo:burn_rate:fast)`), then distribute `flag.json` via configmap/secret.
-- Downstream services can read `retention.json` to temporarily raise retention
- windows during incident mode.
-
-## Determinism
-- Timestamps are UTC ISO-8601; no network dependencies.
-- State is contained under the chosen `state-dir` for reproducible runs.
-
-## Clearing / reset
-- Cooldown counter increments only when burn stays below reset threshold.
-- Once cooldown minutes are met, `flag.json` flips `enabled=false` and the script
- leaves prior retention files untouched (downstream can prune separately).
diff --git a/devops/observability/policy-playbook.md b/devops/observability/policy-playbook.md
deleted file mode 100644
index 311bfb188..000000000
--- a/devops/observability/policy-playbook.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Policy Pipeline Playbook
-
-Scope: policy compile → simulation → approval → promotion path.
-
-## Dashboards
-- Grafana: import `ops/devops/observability/grafana/policy-pipeline.json` (datasource `Prometheus`).
-- Key tiles: Compile p99, Simulation Queue Depth, Approval p95, Promotion Success Rate, Promotion Outcomes.
-
-## Alerts (Prometheus)
-- Rules: `ops/devops/observability/policy-alerts.yaml`
- - `PolicyCompileLatencyP99High` (p99 > 5s for 10m)
- - `PolicySimulationQueueBacklog` (queue depth > 100 for 10m)
- - `PolicyApprovalLatencyHigh` (p95 > 30s for 15m)
- - `PolicyPromotionFailureRate` (failures >20% over 15m)
- - `PolicyPromotionStall` (no successes while queue non-empty for 10m)
-
-## Runbook
-1. **Compile latency alert**
- - Check build nodes for CPU cap; verify cache hits for policy engine.
- - Roll restart single runner; if persists, scale policy compile workers (+1) or purge stale cache.
-2. **Simulation backlog**
- - Inspect queue per stage (panel "Queue Depth by Stage").
- - If queue limited to one stage, increase concurrency for that stage or drain stuck items; otherwise, add workers.
-3. **Approval latency high**
- - Look for blocked approvals (UI/API outages). Re-run approval service health check; fail over to standby.
-4. **Promotion failure rate/stall**
- - Pull recent logs for promotion job; compare failure reasons (policy validation vs. target registry).
- - If registry errors, pause promotions and file incident with registry owner; if policy validation, revert latest policy change or apply override to unblock critical tenants.
-5. **Verification**
- - After mitigation, ensure promotion success rate gauge recovers >95% and queues drain to baseline (<10).
-
-## Escalation
-- Primary: Policy On-Call (week N roster).
-- Secondary: DevOps Guild (release).
-- Page if two critical alerts fire concurrently or any critical alert lasts >30m.
-
-## Notes
-- Metrics assumed available: `policy_compile_duration_seconds_bucket`, `policy_simulation_queue_depth`, `policy_approval_latency_seconds_bucket`, `policy_promotion_outcomes_total{outcome=*}`.
-- Keep alert thresholds stable unless load profile changes; adjust in Git with approval from Policy + DevOps leads.
diff --git a/devops/observability/prometheus/rules/unknowns-queue-alerts.yaml b/devops/observability/prometheus/rules/unknowns-queue-alerts.yaml
deleted file mode 100644
index df7911738..000000000
--- a/devops/observability/prometheus/rules/unknowns-queue-alerts.yaml
+++ /dev/null
@@ -1,186 +0,0 @@
-# Unknowns Queue Alert Rules
-# Sprint: SPRINT_20260118_018_Unknowns_queue_enhancement (UQ-007)
-#
-# Deploy to Prometheus/Alertmanager
-
-groups:
- - name: unknowns-queue
- interval: 1m
- rules:
- # =============================================================================
- # SLA Alerts
- # =============================================================================
-
- - alert: UnknownsSlaBreachCritical
- expr: unknowns_sla_compliance < 0.80
- for: 5m
- labels:
- severity: critical
- team: security
- annotations:
- summary: "SLA compliance dropped below 80%"
- description: |
- SLA compliance is {{ $value | humanizePercentage }}.
- Multiple unknowns have breached their SLA deadlines.
- Immediate action required.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#sla-breach"
-
- - alert: UnknownsSlaBreachWarning
- expr: unknowns_sla_compliance < 0.95 and unknowns_sla_compliance >= 0.80
- for: 15m
- labels:
- severity: warning
- team: security
- annotations:
- summary: "SLA compliance below 95%"
- description: |
- SLA compliance is {{ $value | humanizePercentage }}.
- Some unknowns are approaching or have breached SLA.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#sla-warning"
-
- - alert: UnknownsSlaBreach
- expr: increase(unknowns_sla_breach_total[1h]) > 0
- for: 0m
- labels:
- severity: critical
- team: security
- annotations:
- summary: "Unknown SLA breached"
- description: |
- {{ $value }} unknown(s) have breached SLA in the last hour.
- Check the unknowns queue dashboard for affected entries.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#sla-breach"
-
- # =============================================================================
- # Queue Depth Alerts
- # =============================================================================
-
- - alert: UnknownsHotQueueHigh
- expr: unknowns_queue_depth_hot > 5
- for: 10m
- labels:
- severity: critical
- team: security
- annotations:
- summary: "High number of HOT unknowns"
- description: |
- {{ $value }} HOT unknowns in queue.
- HOT unknowns have 24-hour SLA and block releases.
- Prioritize resolution immediately.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#hot-queue"
-
- - alert: UnknownsHotQueuePresent
- expr: unknowns_queue_depth_hot > 0
- for: 1h
- labels:
- severity: warning
- team: security
- annotations:
- summary: "HOT unknowns present for over 1 hour"
- description: |
- {{ $value }} HOT unknown(s) have been in queue for over 1 hour.
- 50% of 24-hour SLA elapsed.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#hot-queue"
-
- - alert: UnknownsQueueBacklog
- expr: (unknowns_queue_depth_hot + unknowns_queue_depth_warm + unknowns_queue_depth_cold) > 100
- for: 30m
- labels:
- severity: warning
- team: operations
- annotations:
- summary: "Unknowns queue backlog growing"
- description: |
- Total queue depth is {{ $value }}.
- Consider scaling processing capacity or reviewing automation.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#backlog"
-
- # =============================================================================
- # Processing Alerts
- # =============================================================================
-
- - alert: UnknownsStuckProcessing
- expr: greyqueue_processing_count > 10
- for: 30m
- labels:
- severity: warning
- team: operations
- annotations:
- summary: "Many entries stuck in processing"
- description: |
- {{ $value }} entries in Processing status for extended period.
- Check for processing bottlenecks or failures.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#stuck-processing"
-
- - alert: UnknownsProcessingTimeout
- expr: increase(greyqueue_timeout_total[1h]) > 5
- for: 0m
- labels:
- severity: warning
- team: operations
- annotations:
- summary: "Processing timeouts occurring"
- description: |
- {{ $value }} processing timeouts in the last hour.
- Entries are being forcefully retried.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#timeouts"
-
- - alert: UnknownsProcessingFailures
- expr: increase(greyqueue_watchdog_failed_total[1h]) > 0
- for: 0m
- labels:
- severity: critical
- team: operations
- annotations:
- summary: "Processing failures detected"
- description: |
- {{ $value }} entries moved to Failed status in the last hour.
- Manual intervention may be required.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#failures"
-
- # =============================================================================
- # Escalation Alerts
- # =============================================================================
-
- - alert: UnknownsEscalationRate
- expr: increase(unknowns_escalated_total[1h]) > 10
- for: 0m
- labels:
- severity: warning
- team: security
- annotations:
- summary: "High escalation rate"
- description: |
- {{ $value }} unknowns escalated in the last hour.
- Review escalation criteria or upstream data quality.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#escalations"
-
- # =============================================================================
- # Service Health Alerts
- # =============================================================================
-
- - alert: UnknownsSlaMonitorDown
- expr: absent(unknowns_queue_depth_hot) and absent(unknowns_queue_depth_warm)
- for: 5m
- labels:
- severity: critical
- team: operations
- annotations:
- summary: "Unknowns SLA monitor not reporting"
- description: |
- No metrics received from unknowns SLA monitor.
- Check if the service is running.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#service-down"
-
- - alert: UnknownsHealthCheckUnhealthy
- expr: probe_success{job="unknowns-healthcheck"} == 0
- for: 5m
- labels:
- severity: critical
- team: operations
- annotations:
- summary: "Unknowns service health check failing"
- description: |
- Health check endpoint returning unhealthy.
- SLA breaches may exist.
- runbook_url: "https://docs.stella-ops.org/operations/unknowns-queue-runbook#health-check"
diff --git a/devops/observability/signals-playbook.md b/devops/observability/signals-playbook.md
deleted file mode 100644
index 9a79ba3e1..000000000
--- a/devops/observability/signals-playbook.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Signals Pipeline Playbook
-
-Scope: Signals ingestion, cache, scoring, and sensor freshness.
-
-## Dashboards
-- Grafana: import `ops/devops/observability/grafana/signals-pipeline.json` (datasource `Prometheus`).
-- Key tiles: Scoring p95, Cache hit ratio, Sensor staleness, Ingestion outcomes.
-
-## Alerts
-- Rules: `ops/devops/observability/signals-alerts.yaml`
- - `SignalsScoringLatencyP95High` (p95 > 2s for 10m)
- - `SignalsCacheMissRateHigh` (miss ratio >30% for 10m)
- - `SignalsCacheDown`
- - `SignalsSensorStaleness` (no update >15m)
- - `SignalsIngestionErrorRate` (failures >5%)
-
-## Runbook
-1. **Scoring latency high**
- - Check Mongo/Redis health; inspect CPU on workers.
- - Scale Signals API pods or increase cache TTL to reduce load.
-2. **Cache miss rate / cache down**
- - Validate Redis connectivity/ACL; flush not recommended unless key explosion.
- - Increase cache TTL; ensure connection string matches deployment.
-3. **Sensor staleness**
- - Identify stale sensors from alert label; verify upstream pipeline/log shipping.
- - If sensor retired, update allowlist to silence expected gaps.
-4. **Ingestion errors**
- - Tail ingestion logs; classify errors (schema vs. storage).
- - If artifacts rejected, check storage path and disk fullness; add capacity or rotate.
-5. **Verification**
- - Ensure cache hit ratio >90%, scoring p95 <2s, staleness panel near baseline (<5m) after mitigation.
-
-## Escalation
-- Primary: Signals on-call.
-- Secondary: DevOps Guild (observability).
-- Page when critical alerts persist >20m or when cache down + scoring latency co-occur.
-
-## Notes
-- Metrics expected: `signals_reachability_scoring_duration_seconds_bucket`, `signals_cache_hits_total`, `signals_cache_misses_total`, `signals_cache_available`, `signals_sensor_last_seen_timestamp_seconds`, `signals_ingestion_total`, `signals_ingestion_failures_total`.
-- Keep thresholds version-controlled; align with Policy Engine consumers if scoring SLAs change.
diff --git a/devops/offline/feeds/manifest.json b/devops/offline/feeds/manifest.json
deleted file mode 100644
index a55952cb6..000000000
--- a/devops/offline/feeds/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "generated_utc": "2025-11-18T21:41:23.244597Z",
- "summary": "Offline feed bundles registered here. Add entries when baking air-gap bundles.",
- "feeds": [
- {
- "name": "telemetry-offline-bundle",
- "path": "offline/feeds/telemetry-offline-bundle.tar.gz",
- "sha256": "49d3ac3502bad1caaed4c1f7bceaa4ce40fdfce6210d4ae20c90386aeb84ca4e",
- "description": "Telemetry offline bundle (migrated from out/telemetry)"
- }
- ]
-}
\ No newline at end of file
diff --git a/devops/offline/feeds/telemetry-offline-bundle.tar.gz b/devops/offline/feeds/telemetry-offline-bundle.tar.gz
deleted file mode 100644
index 724a3e7aa..000000000
Binary files a/devops/offline/feeds/telemetry-offline-bundle.tar.gz and /dev/null differ
diff --git a/devops/offline/feeds/telemetry-offline-bundle.tar.gz.sha256 b/devops/offline/feeds/telemetry-offline-bundle.tar.gz.sha256
deleted file mode 100644
index d1667d92a..000000000
--- a/devops/offline/feeds/telemetry-offline-bundle.tar.gz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-49d3ac3502bad1caaed4c1f7bceaa4ce40fdfce6210d4ae20c90386aeb84ca4e telemetry-offline-bundle.tar.gz
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/SHA256SUMS b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/SHA256SUMS
deleted file mode 100644
index 06ddbc656..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/SHA256SUMS
+++ /dev/null
@@ -1,4 +0,0 @@
-bb1da224c09031996224154611f2e1c2143c23b96ab583191766f7d281b20800 hashes.sha256
-421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18 sample-sbom-context.json
-e5aecfba5cee8d412408fb449f12fa4d5bf0a7cb7e5b316b99da3b9019897186 sample-vuln-output.ndjson
-736efd36508de7b72c9cbddf851335d9534c326af1670be7d101cbb91634357d sbom-context-response.json
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/hashes.sha256 b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/hashes.sha256
deleted file mode 100644
index 7efa1ccf7..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/hashes.sha256
+++ /dev/null
@@ -1,2 +0,0 @@
-421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18 out/console/guardrails/cli-vuln-29-001/sample-sbom-context.json
-e5aecfba5cee8d412408fb449f12fa4d5bf0a7cb7e5b316b99da3b9019897186 out/console/guardrails/cli-vuln-29-001/sample-vuln-output.ndjson
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sample-sbom-context.json b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sample-sbom-context.json
deleted file mode 100644
index 6ba9b839f..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sample-sbom-context.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "schema": "stellaops.sbom.context/1.0",
- "input": "sbom.json",
- "generated": "2025-11-19T00:00:00Z",
- "packages": [
- {"name": "openssl", "version": "1.1.1w", "purl": "pkg:deb/openssl@1.1.1w"},
- {"name": "zlib", "version": "1.2.11", "purl": "pkg:deb/zlib@1.2.11"}
- ]
-}
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sample-vuln-output.ndjson b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sample-vuln-output.ndjson
deleted file mode 100644
index 65b5ef332..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sample-vuln-output.ndjson
+++ /dev/null
@@ -1 +0,0 @@
-{"command":"stella vuln scan","version":"0.1.0","tenant":"demo","input":"sbom.json","generated":"2025-11-19T00:00:00Z","summary":{"packages":3,"vulnerabilities":2},"vulnerabilities":[{"id":"CVE-2024-1234","package":"openssl","version":"1.1.1w","severity":"HIGH","source":"nvd","path":"/usr/lib/libssl.so"},{"id":"CVE-2024-2345","package":"zlib","version":"1.2.11","severity":"MEDIUM","source":"nvd","path":"/usr/lib/libz.so"}],"provenance":{"sbom_digest":"sha256:dummy-sbom","profile":"offline","evidence_bundle":"mirror-thin-m0-sample"}}
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sbom-context-response.json b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sbom-context-response.json
deleted file mode 100644
index ac1a29061..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-05/sbom-context-response.json
+++ /dev/null
@@ -1 +0,0 @@
-{"schema":"stellaops.sbom.context/1.0","generated":"2025-11-19T00:00:00Z","packages":[{"name":"openssl","version":"1.1.1w","purl":"pkg:deb/openssl@1.1.1w"},{"name":"zlib","version":"1.2.11","purl":"pkg:deb/zlib@1.2.11"}],"timeline":8,"dependencyPaths":5,"hash":"sha256:421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18"}
\ No newline at end of file
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/SHA256SUMS b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/SHA256SUMS
deleted file mode 100644
index f301e1035..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/SHA256SUMS
+++ /dev/null
@@ -1,4 +0,0 @@
-bb1da224c09031996224154611f2e1c2143c23b96ab583191766f7d281b20800 hashes.sha256
-421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18 sample-sbom-context.json
-e5aecfba5cee8d412408fb449f12fa4d5bf0a7cb7e5b316b99da3b9019897186 sample-vuln-output.ndjson
-1f8df765be98c193ac6fa52af778e2e0ec24a7c5acbdfe7a4a461d45bf98f573 sbom-context-response.json
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/hashes.sha256 b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/hashes.sha256
deleted file mode 100644
index 7efa1ccf7..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/hashes.sha256
+++ /dev/null
@@ -1,2 +0,0 @@
-421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18 out/console/guardrails/cli-vuln-29-001/sample-sbom-context.json
-e5aecfba5cee8d412408fb449f12fa4d5bf0a7cb7e5b316b99da3b9019897186 out/console/guardrails/cli-vuln-29-001/sample-vuln-output.ndjson
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sample-sbom-context.json b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sample-sbom-context.json
deleted file mode 100644
index 6ba9b839f..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sample-sbom-context.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "schema": "stellaops.sbom.context/1.0",
- "input": "sbom.json",
- "generated": "2025-11-19T00:00:00Z",
- "packages": [
- {"name": "openssl", "version": "1.1.1w", "purl": "pkg:deb/openssl@1.1.1w"},
- {"name": "zlib", "version": "1.2.11", "purl": "pkg:deb/zlib@1.2.11"}
- ]
-}
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sample-vuln-output.ndjson b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sample-vuln-output.ndjson
deleted file mode 100644
index 65b5ef332..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sample-vuln-output.ndjson
+++ /dev/null
@@ -1 +0,0 @@
-{"command":"stella vuln scan","version":"0.1.0","tenant":"demo","input":"sbom.json","generated":"2025-11-19T00:00:00Z","summary":{"packages":3,"vulnerabilities":2},"vulnerabilities":[{"id":"CVE-2024-1234","package":"openssl","version":"1.1.1w","severity":"HIGH","source":"nvd","path":"/usr/lib/libssl.so"},{"id":"CVE-2024-2345","package":"zlib","version":"1.2.11","severity":"MEDIUM","source":"nvd","path":"/usr/lib/libz.so"}],"provenance":{"sbom_digest":"sha256:dummy-sbom","profile":"offline","evidence_bundle":"mirror-thin-m0-sample"}}
diff --git a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sbom-context-response.json b/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sbom-context-response.json
deleted file mode 100644
index 3085a3b46..000000000
--- a/devops/offline/fixtures/advisory-ai/fixtures/sbom-context/2025-12-08/sbom-context-response.json
+++ /dev/null
@@ -1 +0,0 @@
-{"schema":"stellaops.sbom.context/1.0","generated":"2025-12-08T15:34:22.6874898+00:00","artifactId":"ghcr.io/stellaops/sample-api","purl":"pkg:npm/lodash@4.17.21","versions":[{"version":"2025.11.16.1","firstObserved":"2025-11-16T12:00:00+00:00","lastObserved":"2025-11-16T12:00:00+00:00","status":"observed","source":"scanner:surface_bundle_mock_v1.tgz","isFixAvailable":false,"metadata":{"provenance":"scanner:surface_bundle_mock_v1.tgz","digest":"sha256:112","source_bundle_hash":"sha256:bundle112"}},{"version":"2025.11.15.1","firstObserved":"2025-11-15T12:00:00+00:00","lastObserved":"2025-11-15T12:00:00+00:00","status":"observed","source":"scanner:surface_bundle_mock_v1.tgz","isFixAvailable":false,"metadata":{"provenance":"scanner:surface_bundle_mock_v1.tgz","digest":"sha256:111","source_bundle_hash":"sha256:bundle111"}}],"dependencyPaths":[{"nodes":[{"identifier":"sample-api","version":null},{"identifier":"rollup","version":null},{"identifier":"lodash","version":null}],"isRuntime":false,"source":"sbom.paths","metadata":{"environment":"prod","path_length":"3","artifact":"ghcr.io/stellaops/sample-api@sha256:111","nearest_safe_version":"pkg:npm/lodash@4.17.22","blast_radius":"low","scope":"build"}},{"nodes":[{"identifier":"sample-api","version":null},{"identifier":"express","version":null},{"identifier":"lodash","version":null}],"isRuntime":true,"source":"sbom.paths","metadata":{"environment":"prod","path_length":"3","artifact":"ghcr.io/stellaops/sample-api@sha256:111","nearest_safe_version":"pkg:npm/lodash@4.17.22","blast_radius":"medium","scope":"runtime"}}],"environmentFlags":{"prod":"2"},"blastRadius":{"impactedAssets":2,"impactedWorkloads":1,"impactedNamespaces":1,"impactedPercentage":0.5,"metadata":{"path_sample_count":"2","blast_radius_tags":"low,medium"}},"metadata":{"generated_at":"2025-12-08T15:34:22.6874898+00:00","artifact":"ghcr.io/stellaops/sample-api","version_count":"2","dependency_count":"2","source":"sbom-service","environment_flag_count":"1","blast_radius_present":"True"},"hash":"sha256:0c705259fdf984bf300baba0abf484fc3bbae977cf8a0a2d1877481f552d600d"}
\ No newline at end of file
diff --git a/devops/offline/fixtures/notifier/artifact-hashes.json b/devops/offline/fixtures/notifier/artifact-hashes.json
deleted file mode 100644
index 4e5334798..000000000
--- a/devops/offline/fixtures/notifier/artifact-hashes.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "hash_algorithm": "blake3-256",
- "entries": [
- { "path": "docs/notifications/schemas/notify-schemas-catalog.json", "digest": "34e8655b0c7ca70c844d4b9aee56bdd7bd30b6a8666d2af75a70856b16f5605d" },
- { "path": "docs/notifications/schemas/notify-schemas-catalog.dsse.json", "digest": "7c537ff728312cefb0769568bd376adc2bd79f6926173bf21f50c873902133dc" },
- { "path": "docs/notifications/gaps-nr1-nr10.md", "digest": "b889dfd19a9d0a0f7bafb958135fde151e63c1e5259453d592d6519ae1667819" },
- { "path": "docs/notifications/fixtures/rendering/index.ndjson", "digest": "3a41e62687b6e04f50e86ea74706eeae28eef666d7c4dbb5dc2281e6829bf41a" },
- { "path": "docs/notifications/fixtures/redaction/sample.json", "digest": "dd4eefc8dded5d6f46c832e959ba0eef95ee8b77f10ac0aae90f7c89ad42906c" },
- { "path": "docs/notifications/operations/dashboards/notify-slo.json", "digest": "8b380cb5491727a3ec69d50789f5522ac66c97804bebbf7de326568e52b38fa9" },
- { "path": "docs/notifications/operations/alerts/notify-slo-alerts.yaml", "digest": "2c3b702c42d3e860c7f4e51d577f77961e982e1d233ef5ec392cba5414a0056d" },
- { "path": "offline/notifier/notify-kit.manifest.json", "digest": "15e0b2f670e6b8089c6c960e354f16ba8201d993a077a28794a30b8d1cb23e9a" },
- { "path": "offline/notifier/notify-kit.manifest.dsse.json", "digest": "68742f4e5bd202afe2cc90964d51fea7971395f3e57a875ae7111dcbb760321e" }
- ]
-}
diff --git a/devops/offline/fixtures/notifier/notify-kit.manifest.dsse.json b/devops/offline/fixtures/notifier/notify-kit.manifest.dsse.json
deleted file mode 100644
index e033fcbc0..000000000
--- a/devops/offline/fixtures/notifier/notify-kit.manifest.dsse.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "payloadType": "application/vnd.notify.manifest+json",
- "payload": "ewogICJzY2hlbWFfdmVyc2lvbiI6ICJ2MS4wIiwKICAiZ2VuZXJhdGVkX2F0IjogIjIwMjUtMTItMDRUMDA6MDA6MDBaIiwKICAidGVuYW50X3Njb3BlIjogIioiLAogICJlbnZpcm9ubWVudCI6ICJvZmZsaW5lIiwKICAiYXJ0aWZhY3RzIjogWwogICAgeyAibmFtZSI6ICJzY2hlbWEtY2F0YWxvZyIsICJwYXRoIjogImRvY3Mvbm90aWZpY2F0aW9ucy9zY2hlbWFzL25vdGlmeS1zY2hlbWFzLWNhdGFsb2cuanNvbiIsICJkaWdlc3QiOiAiMzRlODY1NWIwYzdjYTcwYzg0NGQ0YjlhZWU1NmJkZDdiZDMwYjZhODY2NmQyYWY3NWE3MDg1NmIxNmY1NjA1ZCIgfSwKICAgIHsgIm5hbWUiOiAic2NoZW1hLWNhdGFsb2ctZHNzZSIsICJwYXRoIjogImRvY3Mvbm90aWZpY2F0aW9ucy9zY2hlbWFzL25vdGlmeS1zY2hlbWFzLWNhdGFsb2cuZHNzZS5qc29uIiwgImRpZ2VzdCI6ICI3YzUzN2ZmNzI4MzEyY2VmYjA3Njk1NjhiZDM3NmFkYzJiZDc5ZjY5MjYxNzNiZjIxZjUwYzg3MzkwMjEzM2RjIiB9LAogICAgeyAibmFtZSI6ICJydWxlcyIsICJwYXRoIjogImRvY3Mvbm90aWZpY2F0aW9ucy9nYXBzLW5yMS1ucjEwLm1kIiwgImRpZ2VzdCI6ICJiODg5ZGZkMTlhOWQwYTBmN2JhZmI5NTgxMzVmZGUxNTFlNjNjMWU1MjU5NDUzZDU5MmQ2NTE5YWUxNjY3ODE5IiB9LAogICAgeyAibmFtZSI6ICJmaXh0dXJlcy1yZW5kZXJpbmciLCAicGF0aCI6ICJkb2NzL25vdGlmaWNhdGlvbnMvZml4dHVyZXMvcmVuZGVyaW5nL2luZGV4Lm5kanNvbiIsICJkaWdlc3QiOiAiM2E0MWU2MjY4N2I2ZTA0ZjUwZTg2ZWE3NDcwNmVlYWUyOGVlZjY2NmQ3YzRkYmI1ZGMyMjgxZTY4MjliZjQxYSIgfSwKICAgIHsgIm5hbWUiOiAiZml4dHVyZXMtcmVkYWN0aW9uIiwgInBhdGgiOiAiZG9jcy9ub3RpZmljYXRpb25zL2ZpeHR1cmVzL3JlZGFjdGlvbi9zYW1wbGUuanNvbiIsICJkaWdlc3QiOiAiZGQ0ZWVmYzhkZGVkNWQ2ZjQ2YzgzMmU5NTliYTBlZWY5NWVlOGI3N2YxMGFjMGFhZTkwZjdjODlhZDQyOTA2YyIgfSwKICAgIHsgIm5hbWUiOiAiZGFzaGJvYXJkcyIsICJwYXRoIjogImRvY3Mvbm90aWZpY2F0aW9ucy9vcGVyYXRpb25zL2Rhc2hib2FyZHMvbm90aWZ5LXNsby5qc29uIiwgImRpZ2VzdCI6ICI4YjM4MGNiNTQ5MTcyN2EzZWM2OWQ1MDc4OWY1NTIyYWM2NmM5NzgwNGJlYmJmN2RlMzI2NTY4ZTUyYjM4ZmE5IiB9LAogICAgeyAibmFtZSI6ICJhbGVydHMiLCAicGF0aCI6ICJkb2NzL25vdGlmaWNhdGlvbnMvb3BlcmF0aW9ucy9hbGVydHMvbm90aWZ5LXNsby1hbGVydHMueWFtbCIsICJkaWdlc3QiOiAiMmMzYjcwMmM0MmQzZTg2MGM3ZjRlNTFkNTc3Zjc3OTYxZTk4MmUxZDIzM2VmNWVjMzkyY2JhNTQxNGEwMDU2ZCIgfQogIF0sCiAgImhhc2hfYWxnb3JpdGhtIjogImJsYWtlMy0yNTYiLAogICJjYW5vbmljYWxpemF0aW9uIjogImpzb24tbm9ybWFsaXplZC11dGY4Igp9Cg==",
- "signatures": [
- {
- "sig": "DZwohxh6AOAP7Qf9geoZjw2jTXVU3rR8sYw4mgKpMu0=",
- "keyid": "notify-dev-hmac-001",
- "signedAt": "2025-12-04T21:13:10+00:00"
- }
- ]
-}
diff --git a/devops/offline/fixtures/notifier/notify-kit.manifest.json b/devops/offline/fixtures/notifier/notify-kit.manifest.json
deleted file mode 100644
index a423b86d6..000000000
--- a/devops/offline/fixtures/notifier/notify-kit.manifest.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "schema_version": "v1.0",
- "generated_at": "2025-12-04T00:00:00Z",
- "tenant_scope": "*",
- "environment": "offline",
- "artifacts": [
- { "name": "schema-catalog", "path": "docs/notifications/schemas/notify-schemas-catalog.json", "digest": "34e8655b0c7ca70c844d4b9aee56bdd7bd30b6a8666d2af75a70856b16f5605d" },
- { "name": "schema-catalog-dsse", "path": "docs/notifications/schemas/notify-schemas-catalog.dsse.json", "digest": "7c537ff728312cefb0769568bd376adc2bd79f6926173bf21f50c873902133dc" },
- { "name": "rules", "path": "docs/notifications/gaps-nr1-nr10.md", "digest": "b889dfd19a9d0a0f7bafb958135fde151e63c1e5259453d592d6519ae1667819" },
- { "name": "fixtures-rendering", "path": "docs/notifications/fixtures/rendering/index.ndjson", "digest": "3a41e62687b6e04f50e86ea74706eeae28eef666d7c4dbb5dc2281e6829bf41a" },
- { "name": "fixtures-redaction", "path": "docs/notifications/fixtures/redaction/sample.json", "digest": "dd4eefc8dded5d6f46c832e959ba0eef95ee8b77f10ac0aae90f7c89ad42906c" },
- { "name": "dashboards", "path": "docs/notifications/operations/dashboards/notify-slo.json", "digest": "8b380cb5491727a3ec69d50789f5522ac66c97804bebbf7de326568e52b38fa9" },
- { "name": "alerts", "path": "docs/notifications/operations/alerts/notify-slo-alerts.yaml", "digest": "2c3b702c42d3e860c7f4e51d577f77961e982e1d233ef5ec392cba5414a0056d" }
- ],
- "hash_algorithm": "blake3-256",
- "canonicalization": "json-normalized-utf8"
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-expiry-warning.email.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-expiry-warning.email.en-us.template.json
deleted file mode 100644
index e060a7f78..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-expiry-warning.email.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-expiry-warning-email-en-us",
- "tenantId": "bootstrap",
- "channelType": "email",
- "key": "tmpl-attest-expiry-warning",
- "locale": "en-us",
- "renderMode": "html",
- "format": "email",
- "description": "Expiry warning for attestations approaching their expiration window.",
- "body": "Attestation expiry notice
\nThe attestation for {{payload.subject.repository}} (digest {{payload.subject.digest}}) expires on {{payload.attestation.expiresAt}}.
\n\n - Issued: {{payload.attestation.issuedAt}}
\n - Signer:
{{payload.signer.kid}} ({{payload.signer.algorithm}}) \n - Time remaining: {{expires_in payload.attestation.expiresAt event.ts}}
\n
\nPlease rotate the attestation before expiry using these instructions.
\nConsole: {{payload.links.console}}
\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json
deleted file mode 100644
index 7b14512fd..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-expiry-warning-slack-en-us",
- "tenantId": "bootstrap",
- "channelType": "slack",
- "key": "tmpl-attest-expiry-warning",
- "locale": "en-us",
- "renderMode": "markdown",
- "format": "slack",
- "description": "Slack reminder for attestations approaching their expiration window.",
- "body": ":warning: Attestation for `{{payload.subject.digest}}` expires {{expires_in payload.attestation.expiresAt event.ts}}\nRepo: `{{payload.subject.repository}}`{{#if payload.subject.tag}} ({{payload.subject.tag}}){{/if}}\nSigner: `{{fingerprint payload.signer.kid}}` ({{payload.signer.algorithm}})\nIssued: {{payload.attestation.issuedAt}} · Expires: {{payload.attestation.expiresAt}}\nRenewal steps: {{link \"Docs\" payload.links.docs}} · Console: {{link \"Open\" payload.links.console}}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-16"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-key-rotation.email.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-key-rotation.email.en-us.template.json
deleted file mode 100644
index 34ce23c45..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-key-rotation.email.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-key-rotation-email-en-us",
- "tenantId": "bootstrap",
- "channelType": "email",
- "key": "tmpl-attest-key-rotation",
- "locale": "en-us",
- "renderMode": "html",
- "format": "email",
- "description": "Email bulletin for attestation key rotation or revocation events.",
- "body": "Attestation key rotation notice
\nAuthority rotated or revoked signing keys at {{payload.rotation.executedAt}}.
\n\n - Rotation batch: {{payload.rotation.batchId}}
\n - Impacted services: {{payload.rotation.impactedServices}}
\n - Reason: {{payload.rotation.reason}}
\n
\nRecommended action: {{payload.recommendation}}
\nDocs: Rotation playbook
\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-key-rotation.webhook.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-key-rotation.webhook.en-us.template.json
deleted file mode 100644
index 6bd6f8fb4..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-key-rotation.webhook.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-key-rotation-webhook-en-us",
- "tenantId": "bootstrap",
- "channelType": "webhook",
- "key": "tmpl-attest-key-rotation",
- "locale": "en-us",
- "renderMode": "json",
- "format": "webhook",
- "description": "Webhook payload for attestation key rotation/revocation events.",
- "body": "{\n \"event\": \"authority.keys.rotated\",\n \"tenantId\": \"{{event.tenant}}\",\n \"batchId\": \"{{payload.rotation.batchId}}\",\n \"executedAt\": \"{{payload.rotation.executedAt}}\",\n \"impactedServices\": \"{{payload.rotation.impactedServices}}\",\n \"reason\": \"{{payload.rotation.reason}}\",\n \"links\": {\n \"docs\": \"{{payload.links.docs}}\",\n \"console\": \"{{payload.links.console}}\"\n }\n}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-transparency-anomaly.slack.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-transparency-anomaly.slack.en-us.template.json
deleted file mode 100644
index 50a63feb1..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-transparency-anomaly.slack.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-transparency-anomaly-slack-en-us",
- "tenantId": "bootstrap",
- "channelType": "slack",
- "key": "tmpl-attest-transparency-anomaly",
- "locale": "en-us",
- "renderMode": "markdown",
- "format": "slack",
- "description": "Slack alert for transparency witness anomalies.",
- "body": ":warning: Transparency anomaly detected for `{{payload.subject.digest}}`\nWitness: `{{payload.transparency.witnessId}}` ({{payload.transparency.classification}})\nRekor index: {{payload.transparency.rekorIndex}}\nAnomaly window: {{payload.transparency.windowStart}} → {{payload.transparency.windowEnd}}\nRecommended action: {{payload.recommendation}}\nConsole details: {{link \"Open in Console\" payload.links.console}}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-transparency-anomaly.webhook.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-transparency-anomaly.webhook.en-us.template.json
deleted file mode 100644
index 2b6937a10..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-transparency-anomaly.webhook.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-transparency-anomaly-webhook-en-us",
- "tenantId": "bootstrap",
- "channelType": "webhook",
- "key": "tmpl-attest-transparency-anomaly",
- "locale": "en-us",
- "renderMode": "json",
- "format": "webhook",
- "description": "Webhook payload for Rekor transparency anomalies.",
- "body": "{\n \"event\": \"attestor.transparency.anomaly\",\n \"tenantId\": \"{{event.tenant}}\",\n \"subjectDigest\": \"{{payload.subject.digest}}\",\n \"witnessId\": \"{{payload.transparency.witnessId}}\",\n \"classification\": \"{{payload.transparency.classification}}\",\n \"rekorIndex\": {{payload.transparency.rekorIndex}},\n \"window\": {\n \"start\": \"{{payload.transparency.windowStart}}\",\n \"end\": \"{{payload.transparency.windowEnd}}\"\n },\n \"links\": {\n \"console\": \"{{payload.links.console}}\",\n \"rekor\": \"{{payload.links.rekor}}\"\n },\n \"recommendation\": \"{{payload.recommendation}}\"\n}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.email.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.email.en-us.template.json
deleted file mode 100644
index b13385921..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.email.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-verify-fail-email-en-us",
- "tenantId": "bootstrap",
- "channelType": "email",
- "key": "tmpl-attest-verify-fail",
- "locale": "en-us",
- "renderMode": "html",
- "format": "email",
- "description": "Email notice for attestation verification failures.",
- "body": "Attestation verification failure
\nThe attestation for {{payload.subject.repository}} (digest {{payload.subject.digest}}) failed verification at {{event.ts}}.
\n\n - Reason:
{{payload.failure.reasonCode}} — {{payload.failure.reason}} \n - Signer:
{{payload.signer.kid}} ({{payload.signer.algorithm}}) \n - Rekor entry: {{payload.links.rekor}}
\n - Last valid attestation: Console report
\n
\n{{payload.recommendation}}
\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.slack.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.slack.en-us.template.json
deleted file mode 100644
index cd1669dbe..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.slack.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-verify-fail-slack-en-us",
- "tenantId": "bootstrap",
- "channelType": "slack",
- "key": "tmpl-attest-verify-fail",
- "locale": "en-us",
- "renderMode": "markdown",
- "format": "slack",
- "description": "Slack alert for attestation verification failures with Rekor traceability.",
- "body": ":rotating_light: {{attestation_status_badge payload.failure.status}} verification failed for `{{payload.subject.digest}}`\nSigner: `{{fingerprint payload.signer.kid}}` ({{payload.signer.algorithm}})\nReason: `{{payload.failure.reasonCode}}` — {{payload.failure.reason}}\nLast valid attestation: {{link \"Console\" payload.links.console}}\nRekor entry: {{link \"Transparency log\" payload.links.rekor}}\nRecommended action: {{payload.recommendation}}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.webhook.en-us.template.json b/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.webhook.en-us.template.json
deleted file mode 100644
index 896c03691..000000000
--- a/devops/offline/fixtures/notifier/templates/attestation/tmpl-attest-verify-fail.webhook.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-attest-verify-fail-webhook-en-us",
- "tenantId": "bootstrap",
- "channelType": "webhook",
- "key": "tmpl-attest-verify-fail",
- "locale": "en-us",
- "renderMode": "json",
- "format": "webhook",
- "description": "JSON payload for Pager/SOC integrations on attestation verification failures.",
- "body": "{\n \"event\": \"attestor.verification.failed\",\n \"tenantId\": \"{{event.tenant}}\",\n \"subjectDigest\": \"{{payload.subject.digest}}\",\n \"repository\": \"{{payload.subject.repository}}\",\n \"reasonCode\": \"{{payload.failure.reasonCode}}\",\n \"reason\": \"{{payload.failure.reason}}\",\n \"signer\": {\n \"kid\": \"{{payload.signer.kid}}\",\n \"algorithm\": \"{{payload.signer.algorithm}}\"\n },\n \"rekor\": {\n \"url\": \"{{payload.links.rekor}}\",\n \"uuid\": \"{{payload.rekor.uuid}}\",\n \"index\": {{payload.rekor.index}}\n },\n \"recommendation\": \"{{payload.recommendation}}\"\n}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-12"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json b/devops/offline/fixtures/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json
deleted file mode 100644
index d0ba60ade..000000000
--- a/devops/offline/fixtures/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-api-deprecation-email-en-us",
- "tenantId": "bootstrap",
- "channelType": "email",
- "key": "tmpl-api-deprecation",
- "locale": "en-us",
- "renderMode": "html",
- "format": "email",
- "description": "Email notification for retiring Notifier API versions.",
- "body": "Notifier API deprecation notice
\nThe Notifier API v1 endpoints are scheduled for sunset on {{metadata.sunset}}.
\n\n - Paths affected: {{metadata.paths}}
\n - Scope: notify.*
\n - Replacement: {{metadata.replacement}}
\n
\nAction: {{metadata.action}}
\nDetails: Deprecation bulletin
\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-17"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json b/devops/offline/fixtures/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json
deleted file mode 100644
index c0d4a4ebc..000000000
--- a/devops/offline/fixtures/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-api-deprecation-slack-en-us",
- "tenantId": "bootstrap",
- "channelType": "slack",
- "key": "tmpl-api-deprecation",
- "locale": "en-us",
- "renderMode": "markdown",
- "format": "slack",
- "description": "Slack notice for retiring Notifier API versions.",
- "body": ":warning: Notifier API v1 is being deprecated.\nSunset: {{metadata.sunset}}\nPaths affected: {{metadata.paths}}\nDocs: {{link \"Deprecation details\" metadata.docs}}\nAction: {{metadata.action}}\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-17"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-profile-state.email.en-us.template.json b/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-profile-state.email.en-us.template.json
deleted file mode 100644
index 1526e7f74..000000000
--- a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-profile-state.email.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-risk-profile-state-email-en-us",
- "tenantId": "bootstrap",
- "channelType": "email",
- "key": "tmpl-risk-profile-state",
- "locale": "en-us",
- "renderMode": "html",
- "format": "email",
- "description": "Email notice when risk profiles are published, deprecated, or thresholds change.",
- "body": "Risk profile update
\nProfile {{payload.profile.id}} is now {{payload.state}} (version {{payload.profile.version}}).
\n\n - Thresholds: {{payload.thresholds}}
\n - Owner: {{payload.owner}}
\n - Effective at: {{payload.effectiveAt}}
\n
\nNotes: {{payload.notes}}
\nConsole: View profile
\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-24"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-profile-state.slack.en-us.template.json b/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-profile-state.slack.en-us.template.json
deleted file mode 100644
index 10976d522..000000000
--- a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-profile-state.slack.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-risk-profile-state-slack-en-us",
- "tenantId": "bootstrap",
- "channelType": "slack",
- "key": "tmpl-risk-profile-state",
- "locale": "en-us",
- "renderMode": "markdown",
- "format": "json",
- "description": "Slack notice when risk profiles publish, deprecate, or thresholds change.",
- "body": "*Risk profile {{payload.profile.id}}* is now *{{payload.state}}* (v{{payload.profile.version}})\n• thresholds: {{payload.thresholds}}\n• owner: {{payload.owner}}\n• effective: {{payload.effectiveAt}}\n<{{payload.links.console}}|View profile>",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-24"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-severity-change.email.en-us.template.json b/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-severity-change.email.en-us.template.json
deleted file mode 100644
index 5bf436802..000000000
--- a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-severity-change.email.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-risk-severity-change-email-en-us",
- "tenantId": "bootstrap",
- "channelType": "email",
- "key": "tmpl-risk-severity-change",
- "locale": "en-us",
- "renderMode": "html",
- "format": "email",
- "description": "Email notice for risk severity escalation or downgrade.",
- "body": "Risk severity updated
\nRisk profile {{payload.profile.id}} changed severity from {{payload.previous.severity}} to {{payload.current.severity}} at {{event.ts}}.
\n\n - Asset: {{payload.asset.purl}}
\n - Profile version: {{payload.profile.version}}
\n - Reason: {{payload.reason}}
\n
\nView details: Console
\n",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-24"
- }
-}
diff --git a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-severity-change.slack.en-us.template.json b/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-severity-change.slack.en-us.template.json
deleted file mode 100644
index fb308d0af..000000000
--- a/devops/offline/fixtures/notifier/templates/risk/tmpl-risk-severity-change.slack.en-us.template.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "schemaVersion": "notify.template@1",
- "templateId": "tmpl-risk-severity-change-slack-en-us",
- "tenantId": "bootstrap",
- "channelType": "slack",
- "key": "tmpl-risk-severity-change",
- "locale": "en-us",
- "renderMode": "markdown",
- "format": "json",
- "description": "Slack notice for risk severity escalation or downgrade.",
- "body": "*Risk severity changed* for {{payload.profile.id}}\n• from: {{payload.previous.severity}} → to: {{payload.current.severity}}\n• asset: {{payload.asset.purl}}\n• version: {{payload.profile.version}}\n• reason: {{payload.reason}}\n<{{payload.links.console}}|Open in console>",
- "metadata": {
- "author": "notifications-bootstrap",
- "version": "2025-11-24"
- }
-}
diff --git a/devops/offline/fixtures/notifier/verify_notify_kit.sh b/devops/offline/fixtures/notifier/verify_notify_kit.sh
deleted file mode 100644
index bf2bd8a3f..000000000
--- a/devops/offline/fixtures/notifier/verify_notify_kit.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT=$(cd "$(dirname "$0")" && pwd)
-
-missing=0
-for f in notify-kit.manifest.json notify-kit.manifest.dsse.json artifact-hashes.json; do
- if [ ! -f "$ROOT/$f" ]; then
- echo "[FAIL] missing $f" >&2
- missing=1
- fi
-done
-
-if [ "$missing" -ne 0 ]; then
- exit 1
-fi
-
-python - <<'PY'
-import json, sys, pathlib, base64
-try:
- import blake3
-except ImportError:
- sys.stderr.write("blake3 module missing; install with `python -m pip install blake3`\n")
- sys.exit(1)
-
-if '__file__' in globals() and __file__ not in (None, ''):
- root = pathlib.Path(__file__).resolve().parent
-else:
- root = pathlib.Path.cwd()
-hashes = json.loads((root / "artifact-hashes.json").read_text())
-
-def h(path: pathlib.Path):
- if path.suffix == ".json":
- data = json.dumps(json.loads(path.read_text()), sort_keys=True, separators=(',', ':')).encode()
- else:
- data = path.read_bytes()
- return blake3.blake3(data).hexdigest()
-
-ok = True
-for entry in hashes["entries"]:
- path = root.parent.parent / entry["path"]
- digest = entry["digest"]
- if not path.exists():
- sys.stderr.write(f"[FAIL] missing file {path}\n")
- ok = False
- continue
- actual = h(path)
- if actual != digest:
- sys.stderr.write(f"[FAIL] digest mismatch {path}: expected {digest}, got {actual}\n")
- ok = False
-
-if not ok:
- sys.exit(1)
-
-print("[OK] All artifact hashes verified with blake3.")
-PY
diff --git a/devops/offline/fixtures/telemetry/dashboards/ledger/alerts.yml b/devops/offline/fixtures/telemetry/dashboards/ledger/alerts.yml
deleted file mode 100644
index 1f2922e7c..000000000
--- a/devops/offline/fixtures/telemetry/dashboards/ledger/alerts.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-groups:
- - name: ledger-observability
- interval: 30s
- rules:
- - alert: LedgerWriteLatencyHighP95
- expr: histogram_quantile(0.95, sum(rate(ledger_write_latency_seconds_bucket[5m])) by (le, tenant)) > 0.12
- for: 10m
- labels:
- severity: warning
- annotations:
- summary: "Ledger write latency p95 high (tenant {{ $labels.tenant }})"
- description: "ledger_write_latency_seconds p95 > 120ms for >10m. Check DB/queue."
-
- - alert: ProjectionLagHigh
- expr: max_over_time(ledger_projection_lag_seconds[10m]) > 30
- for: 10m
- labels:
- severity: critical
- annotations:
- summary: "Ledger projection lag high"
- description: "projection lag over 30s; projections falling behind ingest."
-
- - alert: MerkleAnchorFailures
- expr: sum(rate(ledger_merkle_anchor_failures_total[15m])) by (tenant, reason) > 0
- for: 15m
- labels:
- severity: critical
- annotations:
- summary: "Merkle anchor failures (tenant {{ $labels.tenant }})"
- description: "Anchoring failures detected (reason={{ $labels.reason }}). Investigate signing/storage."
-
- - alert: AttachmentFailures
- expr: sum(rate(ledger_attachments_encryption_failures_total[10m])) by (tenant, stage) > 0
- for: 10m
- labels:
- severity: warning
- annotations:
- summary: "Attachment pipeline failures (tenant {{ $labels.tenant }}, stage {{ $labels.stage }})"
- description: "Attachment encryption/sign/upload reported failures in the last 10m."
diff --git a/devops/offline/fixtures/telemetry/dashboards/ledger/ledger-observability.json b/devops/offline/fixtures/telemetry/dashboards/ledger/ledger-observability.json
deleted file mode 100644
index b1e675a61..000000000
--- a/devops/offline/fixtures/telemetry/dashboards/ledger/ledger-observability.json
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "id": null,
- "title": "StellaOps Findings Ledger",
- "timezone": "utc",
- "schemaVersion": 39,
- "version": 1,
- "refresh": "30s",
- "tags": ["ledger", "findings", "stellaops"],
- "panels": [
- {
- "type": "timeseries",
- "title": "Ledger Write Latency (P50/P95)",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
- "targets": [
- { "expr": "histogram_quantile(0.5, sum(rate(ledger_write_latency_seconds_bucket{tenant=\"$tenant\"}[5m])) by (le))", "legendFormat": "p50" },
- { "expr": "histogram_quantile(0.95, sum(rate(ledger_write_latency_seconds_bucket{tenant=\"$tenant\"}[5m])) by (le))", "legendFormat": "p95" }
- ],
- "fieldConfig": { "defaults": { "unit": "s" } }
- },
- {
- "type": "timeseries",
- "title": "Write Throughput",
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
- "targets": [
- { "expr": "sum(rate(ledger_events_total{tenant=\"$tenant\"}[5m])) by (event_type)", "legendFormat": "{{event_type}}" }
- ],
- "fieldConfig": { "defaults": { "unit": "ops" } }
- },
- {
- "type": "timeseries",
- "title": "Projection Lag",
- "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
- "targets": [
- { "expr": "max(ledger_projection_lag_seconds{tenant=\"$tenant\"})", "legendFormat": "lag" }
- ],
- "fieldConfig": { "defaults": { "unit": "s" } }
- },
- {
- "type": "timeseries",
- "title": "Merkle Anchor Duration",
- "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
- "targets": [
- { "expr": "histogram_quantile(0.95, sum(rate(ledger_merkle_anchor_duration_seconds_bucket{tenant=\"$tenant\"}[5m])) by (le))", "legendFormat": "p95" }
- ],
- "fieldConfig": { "defaults": { "unit": "s" } }
- },
- {
- "type": "stat",
- "title": "Merkle Anchor Failures (5m)",
- "gridPos": { "h": 4, "w": 6, "x": 0, "y": 16 },
- "targets": [
- { "expr": "sum(rate(ledger_merkle_anchor_failures_total{tenant=\"$tenant\"}[5m]))", "legendFormat": "fail/s" }
- ],
- "options": { "reduceOptions": { "calcs": ["lastNotNull"] } }
- },
- {
- "type": "stat",
- "title": "Attachment Failures (5m)",
- "gridPos": { "h": 4, "w": 6, "x": 6, "y": 16 },
- "targets": [
- { "expr": "sum(rate(ledger_attachments_encryption_failures_total{tenant=\"$tenant\"}[5m])) by (stage)", "legendFormat": "{{stage}}" }
- ],
- "options": { "reduceOptions": { "calcs": ["lastNotNull"] } }
- },
- {
- "type": "stat",
- "title": "Ledger Backlog",
- "gridPos": { "h": 4, "w": 6, "x": 12, "y": 16 },
- "targets": [
- { "expr": "sum(ledger_ingest_backlog_events{tenant=\"$tenant\"})", "legendFormat": "events" }
- ]
- }
- ],
- "templating": {
- "list": [
- {
- "name": "tenant",
- "type": "query",
- "label": "Tenant",
- "datasource": null,
- "query": "label_values(ledger_events_total, tenant)",
- "refresh": 1,
- "multi": false,
- "includeAll": false
- }
- ]
- },
- "annotations": { "list": [] },
- "time": { "from": "now-6h", "to": "now" },
- "timepicker": { "refresh_intervals": ["30s", "1m", "5m", "15m", "1h"] }
-}
diff --git a/devops/offline/kit/__pycache__/build_offline_kit.cpython-312.pyc b/devops/offline/kit/__pycache__/build_offline_kit.cpython-312.pyc
deleted file mode 100644
index fc89c15db..000000000
Binary files a/devops/offline/kit/__pycache__/build_offline_kit.cpython-312.pyc and /dev/null differ
diff --git a/devops/offline/kit/__pycache__/mirror_debug_store.cpython-312.pyc b/devops/offline/kit/__pycache__/mirror_debug_store.cpython-312.pyc
deleted file mode 100644
index b9097892d..000000000
Binary files a/devops/offline/kit/__pycache__/mirror_debug_store.cpython-312.pyc and /dev/null differ
diff --git a/devops/ops-deploy/telemetry/certs/ca.crt b/devops/ops-deploy/telemetry/certs/ca.crt
deleted file mode 100644
index 93cfbbb16..000000000
--- a/devops/ops-deploy/telemetry/certs/ca.crt
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIE0TCCArkCFDKF9uZOnv4aZOLZaMxkCQRXh8WaMA0GCSqGSIb3DQEBCwUAMCUx
-IzAhBgNVBAMMGlN0ZWxsYU9wcyBEZXYgVGVsZW1ldHJ5IENBMB4XDTI1MTEwNTEz
-MTQxNloXDTI2MTEwNTEzMTQxNlowJTEjMCEGA1UEAwwaU3RlbGxhT3BzIERldiBU
-ZWxlbWV0cnkgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsyoJs
-EiYwwH+3FeQGxh0C2e3c6QscMy3Vd+RY5RfVjtWjv7aRfCPegOEf9xARzoy+he2c
-42QaBvSnxZ43yDzKMYTwFkGwi1qFF68dqr8gb4iww3kf+YE09XI7zngH185v1NKi
-Mo61iYTkbf3Er6VqYhsDNGVEQQt4g+JXeTHORxmEJUef36ZqLPCGNnRP/HGxvrLH
-FDjUBCpkjhEUoP7Aqm5hbPcC8KUpKerGBirNsbvuhja+qUhglpdsihgdAiWHUrf1
-lUgQAHDAfM8AtG+v6uWu+0LkxIHc31EAMRn46ZpDZP6Paye9vfJdV4GM387vU5Ts
-0ugdn8BX9PAvCxOhqJ2Lp2Es3Umg0bBa9iYB/KUdhDp+WmVCcUGthmx/V03dwhEu
-+Abqdi9J6ngMIBjB7RPOuTZYPgb9y8YdLKDjOMTzIUGLGWk5Q7OhiGMZYowFRa1G
-0ZhOqiV2N9GrCt2wFAqlLEork07zwmeeDfE/7xrkDqc0jNjf8WoLqcVPhsLLpToT
-4oG40WIHdbMmjw5dXoFUcqLWKKkLvo5R9LXbR8zlHDlELlbMX31DH7aOeqlB7Jx+
-Ya9fwNngEalvrci3WT/CV5bfxXAK57U+ffnYuzhrn3S5PQ4eCQ7QNTC+LZEiJ4XP
-X/KygY1aPFWzQkmPkrBgz/5dS5wfLeHO36ckRwIDAQABMA0GCSqGSIb3DQEBCwUA
-A4ICAQBy353C03SUJC38Ukpq5Gwp3xX/MViM9tcv+G25DFNxz7334glgpeVqQ9HD
-r42DwHaJjudWiTEZ73B2cf3Bs1DLpRLFk9AqsNVp+IlFKBRNgWDyev5UnRhDS/c5
-4MbwVr54Sn/6KVy56MEBLanQLgRB9iHhwekZYZpVkKS8gvdvMzkdj0kJJSYaMJSc
-0TzeL6nQHCuczI9lQ8ofV7yj1s3+XerzC3eKrze3iqc6o6J9163e6rPtm20plaEC
-fgo9NCjB9IRlBdsUuzFUYfgqsN7eisGHKXpFeA4D+Ox47v8uBCtK7zxrd3blvgts
-uNdJImGnjSRXB1C2KNjluCIaTvET4a8cq1nFUAlnA4pJXGwlRkJW42ncKUfEeIGN
-YltnLiwwf2PR/NCpFg+dMvrGwHKe0vHJluJi4cuvlnyh7YjEnn/2fDqUBwXfL7wW
-bRq1oC+o6Vd526BwQiysmp8bwkzsoZEgqSXYEiyP/PMBDrHvTWWi7Uj0mFSJfNIK
-r/3XbKCLfaCqZgm5CjFzpgy71aNMJE5NC7lKJNt7P67ZsyBDEYPleNIlTI9CZBY5
-ChaLedsHqEZgMcD3Hj5ETha8gbIf/07bMvFd/P6+lKq7IRwjozBAx7r8xrfepb0E
-OYqSDgxoHRhYoJzAbrY8w3rhmubb9we/HxcYBlunnN20c8lL6g==
------END CERTIFICATE-----
diff --git a/devops/ops-deploy/telemetry/certs/ca.key b/devops/ops-deploy/telemetry/certs/ca.key
deleted file mode 100644
index e8cde1224..000000000
--- a/devops/ops-deploy/telemetry/certs/ca.key
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCsyoJsEiYwwH+3
-FeQGxh0C2e3c6QscMy3Vd+RY5RfVjtWjv7aRfCPegOEf9xARzoy+he2c42QaBvSn
-xZ43yDzKMYTwFkGwi1qFF68dqr8gb4iww3kf+YE09XI7zngH185v1NKiMo61iYTk
-bf3Er6VqYhsDNGVEQQt4g+JXeTHORxmEJUef36ZqLPCGNnRP/HGxvrLHFDjUBCpk
-jhEUoP7Aqm5hbPcC8KUpKerGBirNsbvuhja+qUhglpdsihgdAiWHUrf1lUgQAHDA
-fM8AtG+v6uWu+0LkxIHc31EAMRn46ZpDZP6Paye9vfJdV4GM387vU5Ts0ugdn8BX
-9PAvCxOhqJ2Lp2Es3Umg0bBa9iYB/KUdhDp+WmVCcUGthmx/V03dwhEu+Abqdi9J
-6ngMIBjB7RPOuTZYPgb9y8YdLKDjOMTzIUGLGWk5Q7OhiGMZYowFRa1G0ZhOqiV2
-N9GrCt2wFAqlLEork07zwmeeDfE/7xrkDqc0jNjf8WoLqcVPhsLLpToT4oG40WIH
-dbMmjw5dXoFUcqLWKKkLvo5R9LXbR8zlHDlELlbMX31DH7aOeqlB7Jx+Ya9fwNng
-Ealvrci3WT/CV5bfxXAK57U+ffnYuzhrn3S5PQ4eCQ7QNTC+LZEiJ4XPX/KygY1a
-PFWzQkmPkrBgz/5dS5wfLeHO36ckRwIDAQABAoICABFrFqurRrNKbHV53PM73GfR
-rTNEQMz2ccvfmqLFcVojXHD13gMbdwgyiL8uqi2JW1HHcXULzSb8hYQ2HSV1Z39g
-b4y+SZ/w5E6fXRVKBZtQ8wASrG6nObmrdnkF7r6nqBVI6HTWUOGG++EFH3xI0o1/
-V0bC7ORtBCmBbfswae9n5nAWS/qXUpDId/Snn6ECizmGRkJgTPw+cUGSurEQK64j
-YB4tHFdtB9E2+wY8T+tNW+sHF5Svvu6Rr7EO2LBv63WRRp8YjdujF7qnujxRdCLR
-NJcnmA40qvynfGRfDsWzUswxbaHqhOaRM9HqBNK9KwCgNdaLyj9WP87+D4pGfRN3
-h4Fr4DTFHz96yV4WHACx5PKdOjUyK5a28EsKMfaCA9ky3IU6Np84NMGxOY8/HVet
-MkBFtZsAKOZrocCih1ZbDZRvMg0lEcLwLEL7yObMT2w/aG5M9Ppj+D/B+d4x0c/f
-6I+WRsBH4d3Ynbsicyn0Axuciu3+V44HAiNqccoA78lEGhTMbJ3NG38Y6EIQFbgV
-XwF+pmNyiXx0C2lm56OfRG6/DcizmHX3ID7aRkzg0dM7HYHGGcdgX3nVN7sLPH3e
-2KDi2LOCZdFFW10EFpPN8gygammhrDFFszgdjDkI/FxU8itiHyL25xRFJWwliwGr
-G3taY+NAYn+udaUC3UixAoIBAQDvemYBS//LpGHL/86RTG3EAdgVswSJJCBTmlAC
-qNF7oj843ewwfX2H/EhRTjm/6DH5443b1menqDXce5wTauGpYtu3XPoDWHepTM0J
-FzM8oFdRYNzgqEMOp7oUMULZkyEA6EFMWYSSB+n+Ce7QRPtb3vr4RNAW45nIxAV5
-kKJ5qVsOS9xY79wWt5GglHHsFG6Lu3nYfzCw/w79BRmVu900KaF+ZEHuKi/lfySG
-eSzVUw26cL35QJ73AsBseUUHRLLVqMO8qZmzII6Y0cE3AiZnyBsX1/FmmZwWrw5Y
-Z1TPuEtkM/Dla6oY+Vcu/G86+L936Z6Yr9UP7q1yChbuDHgXAoIBAQC4tk0P02SL
-ucyI6+YF3vQSmXKlWPqqOPExeVTaxtlHKuxWVS7LfsCn6H6WErB2hoRroBsL6vwp
-zCEskSdwq47OmxfAcN3EZ3Bn2E1z457NO7vzEio4uufwtzsOHZnI8CD/ZNOin/km
-M0RWgezYkDeCeO+/Hk7KBhO+Mlb/ZH7Bgb8F+UiqH3HoKnZIJ3Hgf/skakE5hXdb
-sHV6w8/U1QsoioWbmk/8vlPCY2mAQniZtDVwMzIiWgrQWyLLvd061C/Iy/7C2uR+
-87g/SWL8xxHhwLK0GHfHCh9VOZOatJSVdPf1p9eH+Qzw0gskYMChL1MwwtO8YiJP
-kNgrlY31RiNRAoIBAQCukC4i69899klDhuhwiaHJqv50ctXvkeHujyGbjquEz7P+
-I+azQgZrRb8BZWA7P2qOmQ0jHprYX4lDeuc+UD7GVkWK1793CNnREyayZbL3knmT
-3GOlb4HSAPlnFrGAH/uCycoveWFlgVdT0rG+J0qCoXuX1bFJvgavjhPflUqaHJU/
-SpUIT2/DL3R79TlFuW8LdFFROwWnP4URctI/j32jNGV/2F0m2qGnTJK3Y0UHC0+K
-g/w24J//toXFjHCA59bkX+yubYKYTDcltmB9VJfiNr9pFgPlojthXaG7Vzc/Yzux
-gxsqYNzQ75BZs7Dw77nCEw2Eh0dsIbNU2X31cClpAoIBAE9xgOVsmxMJf3HoW89s
-m/cf7lI1WeI6iWoo8BkEa1ETogBjtLOrOXs+IKu1MBZaNrv/aYKPt5LWi/IaICdy
-cgJkbCvFn2wovQy82FserB9DMMwTpPsvUDCU7h5dFtZ4iQivOeL5APSwGhVG3jIq
-nOVN1HeTtnlncbhc+FPxyh66CgmstNcOnTQohyTzaiQPh1mbJaByyeoyk+SQMWQt
-mRX/tgU9smdXCLlTfn2+mRYqjs1KB6cEqSACAo40g+EYf9DSBCmUcbA0bKszihKE
-ICnDcljJKUL/FIjYMabZQgqh+z+5x5ZgxHMTM92ai18H9rTDJsQgRPeJqZ/dO+gh
-GXECggEBAOf4EahtXAnijFG4razL1xZL5ITdJJQkfgZJDvu3sfS0euzbvnINP3uj
-qyB+8C81nBMMbD7StLXxqRYX3yJgcyfayae91rym/MUPx8r9qQbXSI+IqMAkNhHZ
-ciTKGq6uVxarUYtNIbRArvRG8qS4mRkl/jF8X0+t3AkFSyp4qeD2wvRM0LNFzhwO
-oXwipHEXUwzm13mA6O9rgWbYA7R/I0wUJffZVWh0dlKsj+AYkUDH4GJW13vQeodh
-zmB7vVYkC9hlNkH7Df8KP+xN2NCeq/UOHjQwZuOl/lP9WAvATU18sYn5suZieKiI
-JsLb8CGIEEsgMR8I5fIQdaIeFM5zC8c=
------END PRIVATE KEY-----
diff --git a/devops/ops-deploy/telemetry/certs/client.crt b/devops/ops-deploy/telemetry/certs/client.crt
deleted file mode 100644
index 6715a649a..000000000
--- a/devops/ops-deploy/telemetry/certs/client.crt
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFijCCA3KgAwIBAgIURlzXP0kww1npXz/oCffRcM5B7QEwDQYJKoZIhvcNAQEL
-BQAwJTEjMCEGA1UEAwwaU3RlbGxhT3BzIERldiBUZWxlbWV0cnkgQ0EwHhcNMjUx
-MTA1MTMxNDE4WhcNMjYxMTA1MTMxNDE4WjAgMR4wHAYDVQQDDBVzdGVsbGFvcHMt
-b3RlbC1jbGllbnQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCh0uCg
-HSZRLFXeGx0444w0Ig9+gplsaf+Jf2nK7KAMAOqRadozjzECeK0wopuZ7gCmCtRA
-XBfKxJpoz50poMR44emL7ETbDqFHRW1zfERQfU17LpOd4Sw++BULDQHobB2/2nRg
-Fe2s1gPJKfLnN/u5b8CWWNu0iRl2buaoM9tsXY7XFZ4VK/R23MAlUwm+dwMGu256
-8dGnf6Htmm2uypPEAq8MfJhcnix2BRG7JPi1FR4ZubXut/k0qN1EvWOfZEHVQmxN
-PbcPDV8D/pqGIG11Yiz2aaAxQcwm2V++fh9bwE+ZC6wtcGi4jcSZ2OOTAZht2V1S
-ZEw6M0dOrvoS8s76vfiBKxF5HzTImD/ysfC/9EHTs+3EUK25p8ZKDjoisKP7DwZ1
-7IhxFZ6vkHv+AAaOera6JdsbquCIL6bUg9EDjq7aZOQoBMKTAecbPVEigsrnC4VY
-U4qXH6sr9S1uub1wBe41+4Ae6G6oEWtdfWOBydYBjAVcbrk/LXXOuYk6cP1ajOYQ
-Y0Y0NrIhGhR8k74TtVWfYZqAFDiKPdUI/HWlW0IZnxFqggLgQ+phNoPveQhu9kbe
-nCnp5J+ej/YY5Xey37k6nIDh260ZomlizFnzxG07L457iIxhtpGq27OeMtZVi8yS
-r4xxbEBWge1t3pqk5PzIR7s/qVkvlobTtU1QlwIDAQABo4G2MIGzMBMGA1UdJQQM
-MAoGCCsGAQUFBwMCMDEGA1UdEQQqMCiCFXN0ZWxsYW9wcy1vdGVsLWNsaWVudIIJ
-bG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBTFQj7R63yc+tf1xNK9aO9afwPZDDBK
-BgNVHSMEQzBBoSmkJzAlMSMwIQYDVQQDDBpTdGVsbGFPcHMgRGV2IFRlbGVtZXRy
-eSBDQYIUMoX25k6e/hpk4tlozGQJBFeHxZowDQYJKoZIhvcNAQELBQADggIBAIw0
-q9ulVu3mKpONcyGBf7a104uI45bc8xjihqgbd2ovFpyCORg63ejrvr4IUBzz+7E+
-M4rKZENE6SliI42cXXWiown/g/k75DdRGUF7opjcWMt0OjTU5G8vvhdHc26Xc6Sa
-k/qxbX8qydgPaa9MC2aohY902xwQ4OryQB/vBgukbvEdva/h3DsS3vWz0DPm3TgR
-D/gJZYWu66P1yuljb3q2UOGRUjwhrZSI+0gq4q2yaT85MEXgL+QlFtAiXkVxjS2y
-LRLQ3b7PJjoUZI3msREQyLPphmKJHBx1cfGJc1vxV93ZzjFc8FPnpFRqqNG+xYSl
-8REsB1xjPz1tSEi0mFe226S8xCSgGcAk7wi2Urw+BZiKxpZ8ATXH0awCsl42W1w1
-8oxN9c/8/S6qE3+1LF3QZiFm6I3HDQ61zSHPxbasuI5Y5+c7Z3A1UVTxCGAMUBPE
-zDP3XHwQkV27P3ChlUzP0ohTJgJvny81aIpZGJk/gTloPCNuKxLwVXLnR8qFR01U
-5HtWXkgMkpukh1S4wEDzN6IiLqyWsntoewwe6evqwbLRkUGsiqIHGzTI23B4UvFN
-qBonwFDGulP9t/VH33f+vmLnGv7ERVXRiVTXKts2cVGhGhWLyV+a/H5cF6pJKyet
-W2jYvD5N0Vzpw9IdQCIASSQ1ntYcTwW/CIz0ZkDW
------END CERTIFICATE-----
diff --git a/devops/ops-deploy/telemetry/certs/client.key b/devops/ops-deploy/telemetry/certs/client.key
deleted file mode 100644
index 35aa5eaa5..000000000
--- a/devops/ops-deploy/telemetry/certs/client.key
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-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
-1MtPh2jZeC2RldWjRlBsY1fVC9SQF4k=
------END PRIVATE KEY-----
diff --git a/devops/ops-deploy/telemetry/certs/collector.crt b/devops/ops-deploy/telemetry/certs/collector.crt
deleted file mode 100644
index fe5242004..000000000
--- a/devops/ops-deploy/telemetry/certs/collector.crt
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFmjCCA4KgAwIBAgIURlzXP0kww1npXz/oCffRcM5B7QAwDQYJKoZIhvcNAQEL
-BQAwJTEjMCEGA1UEAwwaU3RlbGxhT3BzIERldiBUZWxlbWV0cnkgQ0EwHhcNMjUx
-MTA1MTMxNDE2WhcNMjYxMTA1MTMxNDE2WjAjMSEwHwYDVQQDDBhzdGVsbGFvcHMt
-b3RlbC1jb2xsZWN0b3IwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCp
-TfZtIbS3A0gvYJY6MkHDW2TBD43+ooDqFFfxNsJTokmAT4InKRtX5ZXHS+Cpamg7
-g+Inre4U4nJ3gj9drGOonSyFi6MbEeYKhu7tRDqm6ryDfZ8AoMddwc8hasA4sajf
-e+PEHvtZFQliCoR83gQa+GHn3y8OSqYoAkeS9cNbK2dPNXjeDLGpQletXuGNtHOZ
-K2J67mhVIacDms8Vc7Up1beJ4Xg4w0XG1WW3sjkQk0KABtAWDv3nYZbF5q0XE3tD
-lqGfg1pdZHARuZc8WqCURjjOFZIZyqKo26JBAtKYylUR2bhrrafYIaw3HgUSj+qO
-m1Xe69P3JXnLLn3/A60S0URDBrVsY3ijXMhvcJV7QIIuGJYahe1J+o4cqtODJOiX
-w8BlEIf5uypo4bNoTxgE7dODST963DncM3VOS6xI+Cn79P1XkWi0VXRruB+RwDCe
-heXX1XHFrO/uvn/bZP66UiBx4sFA84NTqS9j3boQ/SH5ccEnmDvJ1EyhyDhQGgyl
-n/kgOwU0w6j514aexw5eJ/pLAr8o620pBUItgxXK12oaIceGrM3nDAaraXFYfsIF
-xF9V5WDqhtJ4IRJv4eAxUsWYVPgJ0uEYJ1C2eTh5YPktaBiHhCYBpDPSQBy1EJYi
-av4n8reI1gW9sO0t4zHcTZISnZzVbXH4eC7vG/dVuQIDAQABo4HDMIHAMDQGA1Ud
-EQQtMCuCGHN0ZWxsYW9wcy1vdGVsLWNvbGxlY3RvcoIJbG9jYWxob3N0hwR/AAAB
-MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUcCTexBYq
-9yCbUmHps6G/lIOIctkwSgYDVR0jBEMwQaEppCcwJTEjMCEGA1UEAwwaU3RlbGxh
-T3BzIERldiBUZWxlbWV0cnkgQ0GCFDKF9uZOnv4aZOLZaMxkCQRXh8WaMA0GCSqG
-SIb3DQEBCwUAA4ICAQBqtaI8No1BDaScO6/QCKbrsbFYxj/a7o9MwYoIgIldpIeB
-YTN4EbRg8AqqkOcndRFQcX+T7bFIijL0gya4mF3/lY1jIHi/RLLfWSqrrLue00vR
-GaXgiNMU34kJIXdv+sB/46Q3MdTdTLNmF+Y03sBqFIDuBFwUl6GHHZQ93kIeIlSc
-m3Vrb5OAz2dbMwe6EW17hDQ48kyzmWqlp5AfCZml5Oj15JMixYzWB1wvUlgQ58kV
-M7aG8tpUwp6WqpBLpx6zaYcqnb4GvJxFJVPJMdjrB7b8sIi4awGLijrcVdgcDjNo
-1+gfqLca200m88hMa0bGui4LQtcGJxDJLDdE3Ud3isSvDVv62HWUu9YO2DMa3e8e
-FG21zOWTPx/XlOOGonHdULQFETpySU78Xx6ql6lzmLoHDGGrktUTZxXqhKbflTmm
-B3gfujICGW92pF/6dlc2euuk7DaeG7jWmoYvymEi2bkEcY83KiYwqrJzXpb79TE/
-NmbCVocTbdmDV+oDP5qmJhFzBhb2aQjp8Ufxt8eZ6PTTtUS46vZCJuKQEQcezsRD
-G2+YAMshbjNMNA7pv755ykOaZT9vTBSpv7vF2XiiIpXtijXnzthVkOxW96jgubpd
-Sh1DCq2QnuIXRTjsQi9uZqQ1nifxkuYRxEtFb5wvJ8UBwzZRdqP037yaIkQ0MA==
------END CERTIFICATE-----
diff --git a/devops/ops-deploy/telemetry/certs/collector.key b/devops/ops-deploy/telemetry/certs/collector.key
deleted file mode 100644
index e248a5f40..000000000
--- a/devops/ops-deploy/telemetry/certs/collector.key
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-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
-uYZljgYrmHZpQPucWwwmGNsDx+casg==
------END PRIVATE KEY-----
diff --git a/devops/provenance/alerts.yaml b/devops/provenance/alerts.yaml
deleted file mode 100644
index 185d3ce28..000000000
--- a/devops/provenance/alerts.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-groups:
- - name: provenance
- rules:
- - alert: ProvenanceKeyRotationOverdue
- expr: (time() - provenance_last_key_rotation_seconds) > 60*60*24*90
- for: 10m
- labels:
- severity: warning
- team: devops
- annotations:
- summary: "Provenance signing key rotation overdue"
- description: "Last rotation {{ $value }} seconds ago (>90d)."
-
- - alert: ProvenanceSignerFailures
- expr: rate(provenance_sign_failures_total[5m]) > 0
- for: 5m
- labels:
- severity: critical
- team: devops
- annotations:
- summary: "Provenance signer failures detected"
- description: "Signer failure rate non-zero in last 5m."
diff --git a/devops/provenance/grafana/provenance-overview.json b/devops/provenance/grafana/provenance-overview.json
deleted file mode 100644
index bc7438baa..000000000
--- a/devops/provenance/grafana/provenance-overview.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "title": "Provenance Signing",
- "time": { "from": "now-24h", "to": "now" },
- "panels": [
- {
- "type": "stat",
- "title": "Last key rotation (days)",
- "targets": [
- { "expr": "(time() - provenance_last_key_rotation_seconds) / 86400" }
- ]
- },
- {
- "type": "timeseries",
- "title": "Signing failures",
- "targets": [
- { "expr": "rate(provenance_sign_failures_total[5m])", "legendFormat": "failures/s" }
- ]
- }
- ],
- "schemaVersion": 39,
- "version": 1
-}
diff --git a/devops/release/__pycache__/build_release.cpython-312.pyc b/devops/release/__pycache__/build_release.cpython-312.pyc
deleted file mode 100644
index 1dab25409..000000000
Binary files a/devops/release/__pycache__/build_release.cpython-312.pyc and /dev/null differ
diff --git a/devops/release/__pycache__/verify_release.cpython-312.pyc b/devops/release/__pycache__/verify_release.cpython-312.pyc
deleted file mode 100644
index 7a0f262d6..000000000
Binary files a/devops/release/__pycache__/verify_release.cpython-312.pyc and /dev/null differ
diff --git a/devops/releases/2025.09-airgap.yaml b/devops/releases/2025.09-airgap.yaml
index 9b8f72fe6..57fa1aaca 100644
--- a/devops/releases/2025.09-airgap.yaml
+++ b/devops/releases/2025.09-airgap.yaml
@@ -16,18 +16,20 @@ release:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5
- name: concelier
image: registry.stella-ops.org/stellaops/concelier@sha256:29e2e1a0972707e092cbd3d370701341f9fec2aa9316fb5d8100480f2a1c76b5
- - name: excititor
- image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68
- - name: advisory-ai-web
- image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2-airgap
- - name: advisory-ai-worker
- image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2-airgap
- - name: web-ui
- image: registry.stella-ops.org/stellaops/web-ui@sha256:bee9668011ff414572131dc777faab4da24473fe12c230893f161cabee092a1d
+ - name: excititor
+ image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68
+ - name: advisory-ai-web
+ image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2-airgap
+ - name: advisory-ai-worker
+ image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2-airgap
+ - name: web-ui
+ image: registry.stella-ops.org/stellaops/web-ui@sha256:bee9668011ff414572131dc777faab4da24473fe12c230893f161cabee092a1d
infrastructure:
- mongo:
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- minio:
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ postgres:
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
+ valkey:
+ image: docker.io/valkey/valkey@sha256:9a2cf7c980f2f28678a5e34b1c8d74e4b7b7b6c8c4d5e6f7a8b9c0d1e2f3a4b5
+ rustfs:
+ image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
checksums:
releaseManifestSha256: b787b833dddd73960c31338279daa0b0a0dce2ef32bd32ef1aaf953d66135f94
diff --git a/devops/releases/2025.09-mock-dev.yaml b/devops/releases/2025.09-mock-dev.yaml
index 97ff04cfd..60555e16d 100644
--- a/devops/releases/2025.09-mock-dev.yaml
+++ b/devops/releases/2025.09-mock-dev.yaml
@@ -41,9 +41,11 @@ release:
- name: task-runner
image: registry.stella-ops.org/stellaops/task-runner@sha256:eb5ad992b49a41554f41516be1a6afcfa6522faf2111c08ff2b3664ad2fc954b
infrastructure:
- mongo:
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- minio:
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ postgres:
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
+ valkey:
+ image: docker.io/valkey/valkey@sha256:9a2cf7c980f2f28678a5e34b1c8d74e4b7b7b6c8c4d5e6f7a8b9c0d1e2f3a4b5
+ rustfs:
+ image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
checksums:
releaseManifestSha256: dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7
diff --git a/devops/releases/2025.09-stable.yaml b/devops/releases/2025.09-stable.yaml
index b6f301ec1..bc7b9c8a4 100644
--- a/devops/releases/2025.09-stable.yaml
+++ b/devops/releases/2025.09-stable.yaml
@@ -16,18 +16,20 @@ release:
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
- name: concelier
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
- - name: excititor
- image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
- - name: advisory-ai-web
- image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2
- - name: advisory-ai-worker
- image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2
- - name: web-ui
- image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
+ - name: excititor
+ image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
+ - name: advisory-ai-web
+ image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2
+ - name: advisory-ai-worker
+ image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2
+ - name: web-ui
+ image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
infrastructure:
- mongo:
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- minio:
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ postgres:
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
+ valkey:
+ image: docker.io/valkey/valkey@sha256:9a2cf7c980f2f28678a5e34b1c8d74e4b7b7b6c8c4d5e6f7a8b9c0d1e2f3a4b5
+ rustfs:
+ image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
checksums:
releaseManifestSha256: dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7
diff --git a/devops/releases/2025.10-edge.yaml b/devops/releases/2025.10-edge.yaml
index 3ba3bee6e..7e8cb0608 100644
--- a/devops/releases/2025.10-edge.yaml
+++ b/devops/releases/2025.10-edge.yaml
@@ -3,21 +3,21 @@
channel: "edge"
date: "2025-10-01T00:00:00Z"
calendar: "2025.10"
- components:
- - name: authority
- image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
- - name: signer
- image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
+ components:
+ - name: authority
+ image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
+ - name: signer
+ image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
- name: attestor
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
- name: issuer-directory-web
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
- name: scanner-web
image: registry.stella-ops.org/stellaops/scanner-web@sha256:e0dfdb087e330585a5953029fb4757f5abdf7610820a085bd61b457dbead9a11
- - name: scanner-worker
- image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
- - name: concelier
- image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
+ - name: scanner-worker
+ image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
+ - name: concelier
+ image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
- name: excititor
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
- name: advisory-ai-web
@@ -27,10 +27,10 @@
- name: web-ui
image: registry.stella-ops.org/stellaops/web-ui@sha256:38b225fa7767a5b94ebae4dae8696044126aac429415e93de514d5dd95748dcf
infrastructure:
- mongo:
- image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
- minio:
- image: docker.io/minio/minio@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
+ postgres:
+ image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
+ valkey:
+ image: docker.io/valkey/valkey@sha256:9a2cf7c980f2f28678a5e34b1c8d74e4b7b7b6c8c4d5e6f7a8b9c0d1e2f3a4b5
rustfs:
image: registry.stella-ops.org/stellaops/rustfs:2025.10.0-edge
checksums:
diff --git a/devops/risk-bundle/build-bundle.sh b/devops/risk-bundle/build-bundle.sh
deleted file mode 100644
index b217d55cf..000000000
--- a/devops/risk-bundle/build-bundle.sh
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/usr/bin/env bash
-# Risk Bundle Builder Script
-# RISK-BUNDLE-69-002: CI/offline kit pipeline integration
-#
-# Usage: build-bundle.sh --output [--fixtures-only] [--include-osv]
-#
-# This script builds a risk bundle for offline kit distribution.
-# In --fixtures-only mode, it generates a deterministic fixture bundle
-# suitable for CI testing without requiring live provider data.
-
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
-
-# Defaults
-OUTPUT_DIR=""
-FIXTURES_ONLY=false
-INCLUDE_OSV=false
-BUNDLE_ID=""
-
-# Parse arguments
-while [[ $# -gt 0 ]]; do
- case $1 in
- --output)
- OUTPUT_DIR="$2"
- shift 2
- ;;
- --fixtures-only)
- FIXTURES_ONLY=true
- shift
- ;;
- --include-osv)
- INCLUDE_OSV=true
- shift
- ;;
- --bundle-id)
- BUNDLE_ID="$2"
- shift 2
- ;;
- -h|--help)
- echo "Usage: build-bundle.sh --output [--fixtures-only] [--include-osv] [--bundle-id ]"
- echo ""
- echo "Options:"
- echo " --output Output directory for bundle artifacts (required)"
- echo " --fixtures-only Use fixture data instead of live provider downloads"
- echo " --include-osv Include OSV providers (larger bundle)"
- echo " --bundle-id Custom bundle ID (default: auto-generated)"
- exit 0
- ;;
- *)
- echo "Unknown option: $1"
- exit 1
- ;;
- esac
-done
-
-# Validate required arguments
-if [[ -z "$OUTPUT_DIR" ]]; then
- echo "Error: --output is required"
- exit 1
-fi
-
-# Generate bundle ID if not provided
-if [[ -z "$BUNDLE_ID" ]]; then
- BUNDLE_ID="risk-bundle-$(date -u +%Y%m%d-%H%M%S)"
-fi
-
-echo "=== Risk Bundle Builder ==="
-echo "Output directory: $OUTPUT_DIR"
-echo "Bundle ID: $BUNDLE_ID"
-echo "Fixtures only: $FIXTURES_ONLY"
-echo "Include OSV: $INCLUDE_OSV"
-
-# Create output directory
-mkdir -p "$OUTPUT_DIR"
-
-# Create temporary working directory
-WORK_DIR=$(mktemp -d)
-trap "rm -rf $WORK_DIR" EXIT
-
-echo ""
-echo "=== Preparing provider data ==="
-
-# Provider directories
-mkdir -p "$WORK_DIR/providers/cisa-kev"
-mkdir -p "$WORK_DIR/providers/first-epss"
-mkdir -p "$WORK_DIR/manifests"
-mkdir -p "$WORK_DIR/signatures"
-
-# Fixed timestamp for deterministic builds (2024-01-01 00:00:00 UTC)
-FIXED_TIMESTAMP="2024-01-01T00:00:00Z"
-FIXED_EPOCH=1704067200
-
-if [[ "$FIXTURES_ONLY" == "true" ]]; then
- echo "Using fixture data..."
-
- # Create CISA KEV fixture (mandatory provider)
- cat > "$WORK_DIR/providers/cisa-kev/snapshot" <<'EOF'
-{
- "catalogVersion": "2024.12.11",
- "dateReleased": "2024-12-11T00:00:00Z",
- "count": 3,
- "vulnerabilities": [
- {
- "cveID": "CVE-2024-0001",
- "vendorProject": "Example Vendor",
- "product": "Example Product",
- "vulnerabilityName": "Example Vulnerability 1",
- "dateAdded": "2024-01-15",
- "shortDescription": "Test vulnerability for CI fixtures",
- "requiredAction": "Apply updates per vendor instructions",
- "dueDate": "2024-02-05",
- "knownRansomwareCampaignUse": "Unknown"
- },
- {
- "cveID": "CVE-2024-0002",
- "vendorProject": "Another Vendor",
- "product": "Another Product",
- "vulnerabilityName": "Example Vulnerability 2",
- "dateAdded": "2024-02-01",
- "shortDescription": "Another test vulnerability",
- "requiredAction": "Apply updates per vendor instructions",
- "dueDate": "2024-02-22",
- "knownRansomwareCampaignUse": "Known"
- },
- {
- "cveID": "CVE-2024-0003",
- "vendorProject": "Third Vendor",
- "product": "Third Product",
- "vulnerabilityName": "Example Vulnerability 3",
- "dateAdded": "2024-03-01",
- "shortDescription": "Third test vulnerability",
- "requiredAction": "Apply updates per vendor instructions",
- "dueDate": "2024-03-22",
- "knownRansomwareCampaignUse": "Unknown"
- }
- ]
-}
-EOF
-
- # Create FIRST EPSS fixture (optional provider)
- cat > "$WORK_DIR/providers/first-epss/snapshot" <<'EOF'
-{
- "model_version": "v2024.01.01",
- "score_date": "2024-12-11",
- "scores": [
- {"cve": "CVE-2024-0001", "epss": 0.00043, "percentile": 0.08},
- {"cve": "CVE-2024-0002", "epss": 0.00156, "percentile": 0.45},
- {"cve": "CVE-2024-0003", "epss": 0.00089, "percentile": 0.21}
- ]
-}
-EOF
-
- # Include OSV if requested
- if [[ "$INCLUDE_OSV" == "true" ]]; then
- mkdir -p "$WORK_DIR/providers/osv"
- cat > "$WORK_DIR/providers/osv/snapshot" <<'EOF'
-{
- "source": "osv",
- "updated": "2024-12-11T00:00:00Z",
- "advisories": [
- {"id": "GHSA-test-0001", "modified": "2024-01-15T00:00:00Z", "aliases": ["CVE-2024-0001"]},
- {"id": "GHSA-test-0002", "modified": "2024-02-01T00:00:00Z", "aliases": ["CVE-2024-0002"]}
- ]
-}
-EOF
- fi
-
-else
- echo "Live provider download not yet implemented"
- echo "Use --fixtures-only for CI testing"
- exit 1
-fi
-
-echo ""
-echo "=== Computing hashes ==="
-
-# Compute hashes for each provider file
-CISA_HASH=$(sha256sum "$WORK_DIR/providers/cisa-kev/snapshot" | cut -d' ' -f1)
-EPSS_HASH=$(sha256sum "$WORK_DIR/providers/first-epss/snapshot" | cut -d' ' -f1)
-
-echo "cisa-kev hash: $CISA_HASH"
-echo "first-epss hash: $EPSS_HASH"
-
-PROVIDERS_JSON="[
- {\"providerId\": \"cisa-kev\", \"digest\": \"sha256:$CISA_HASH\", \"snapshotDate\": \"$FIXED_TIMESTAMP\", \"optional\": false},
- {\"providerId\": \"first-epss\", \"digest\": \"sha256:$EPSS_HASH\", \"snapshotDate\": \"$FIXED_TIMESTAMP\", \"optional\": true}"
-
-if [[ "$INCLUDE_OSV" == "true" ]]; then
- OSV_HASH=$(sha256sum "$WORK_DIR/providers/osv/snapshot" | cut -d' ' -f1)
- echo "osv hash: $OSV_HASH"
- PROVIDERS_JSON="$PROVIDERS_JSON,
- {\"providerId\": \"osv\", \"digest\": \"sha256:$OSV_HASH\", \"snapshotDate\": \"$FIXED_TIMESTAMP\", \"optional\": true}"
-fi
-
-PROVIDERS_JSON="$PROVIDERS_JSON
-]"
-
-# Compute inputs hash (hash of all provider hashes sorted)
-INPUTS_HASH=$(echo -n "$CISA_HASH$EPSS_HASH" | sha256sum | cut -d' ' -f1)
-echo "inputs hash: $INPUTS_HASH"
-
-echo ""
-echo "=== Creating manifest ==="
-
-# Create provider manifest
-cat > "$WORK_DIR/manifests/provider-manifest.json" </dev/null || base64 "$WORK_DIR/manifests/provider-manifest.json")
- cat > "$WORK_DIR/signatures/provider-manifest.dsse" < /tmp/bundle-files.txt
-
-# Create tar with fixed mtime
-tar --mtime="@$FIXED_EPOCH" \
- --sort=name \
- --owner=0 --group=0 \
- --numeric-owner \
- -cvf "$OUTPUT_DIR/risk-bundle.tar" \
- -T /tmp/bundle-files.txt
-
-# Compress with gzip (deterministic)
-gzip -n -9 < "$OUTPUT_DIR/risk-bundle.tar" > "$OUTPUT_DIR/risk-bundle.tar.gz"
-rm "$OUTPUT_DIR/risk-bundle.tar"
-
-# Copy manifest to output for easy access
-cp "$WORK_DIR/manifests/provider-manifest.json" "$OUTPUT_DIR/manifest.json"
-
-# Compute bundle hash
-BUNDLE_HASH=$(sha256sum "$OUTPUT_DIR/risk-bundle.tar.gz" | cut -d' ' -f1)
-
-echo ""
-echo "=== Build complete ==="
-echo "Bundle: $OUTPUT_DIR/risk-bundle.tar.gz"
-echo "Bundle hash: $BUNDLE_HASH"
-echo "Manifest: $OUTPUT_DIR/manifest.json"
-echo "Manifest hash: $MANIFEST_HASH"
-
-# Create checksum file
-echo "$BUNDLE_HASH risk-bundle.tar.gz" > "$OUTPUT_DIR/risk-bundle.tar.gz.sha256"
-
-echo ""
-echo "=== Artifacts ==="
-ls -la "$OUTPUT_DIR"
diff --git a/devops/risk-bundle/verify-bundle.sh b/devops/risk-bundle/verify-bundle.sh
deleted file mode 100644
index 917ac6191..000000000
--- a/devops/risk-bundle/verify-bundle.sh
+++ /dev/null
@@ -1,332 +0,0 @@
-#!/usr/bin/env bash
-# Risk Bundle Verification Script
-# RISK-BUNDLE-69-002: CI/offline kit pipeline integration
-#
-# Usage: verify-bundle.sh [--signature ] [--strict] [--json]
-#
-# This script verifies a risk bundle for integrity and correctness.
-# Exit codes:
-# 0 - Bundle is valid
-# 1 - Bundle is invalid or verification failed
-# 2 - Input error (missing file, bad arguments)
-
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-
-# Defaults
-BUNDLE_PATH=""
-SIGNATURE_PATH=""
-STRICT_MODE=false
-JSON_OUTPUT=false
-
-# Parse arguments
-while [[ $# -gt 0 ]]; do
- case $1 in
- --signature)
- SIGNATURE_PATH="$2"
- shift 2
- ;;
- --strict)
- STRICT_MODE=true
- shift
- ;;
- --json)
- JSON_OUTPUT=true
- shift
- ;;
- -h|--help)
- echo "Usage: verify-bundle.sh [--signature ] [--strict] [--json]"
- echo ""
- echo "Arguments:"
- echo " Path to risk-bundle.tar.gz (required)"
- echo ""
- echo "Options:"
- echo " --signature Path to detached signature file"
- echo " --strict Fail on any warning (e.g., missing optional providers)"
- echo " --json Output results as JSON"
- echo ""
- echo "Exit codes:"
- echo " 0 - Bundle is valid"
- echo " 1 - Bundle is invalid"
- echo " 2 - Input error"
- exit 0
- ;;
- -*)
- echo "Unknown option: $1"
- exit 2
- ;;
- *)
- if [[ -z "$BUNDLE_PATH" ]]; then
- BUNDLE_PATH="$1"
- else
- echo "Unexpected argument: $1"
- exit 2
- fi
- shift
- ;;
- esac
-done
-
-# Validate required arguments
-if [[ -z "$BUNDLE_PATH" ]]; then
- echo "Error: bundle path is required"
- exit 2
-fi
-
-if [[ ! -f "$BUNDLE_PATH" ]]; then
- echo "Error: bundle not found: $BUNDLE_PATH"
- exit 2
-fi
-
-# Create temporary extraction directory
-WORK_DIR=$(mktemp -d)
-trap "rm -rf $WORK_DIR" EXIT
-
-# Initialize result tracking
-ERRORS=()
-WARNINGS=()
-BUNDLE_ID=""
-BUNDLE_VERSION=""
-PROVIDER_COUNT=0
-MANDATORY_FOUND=false
-
-log_error() {
- ERRORS+=("$1")
- if [[ "$JSON_OUTPUT" != "true" ]]; then
- echo "ERROR: $1" >&2
- fi
-}
-
-log_warning() {
- WARNINGS+=("$1")
- if [[ "$JSON_OUTPUT" != "true" ]]; then
- echo "WARNING: $1" >&2
- fi
-}
-
-log_info() {
- if [[ "$JSON_OUTPUT" != "true" ]]; then
- echo "$1"
- fi
-}
-
-log_info "=== Risk Bundle Verification ==="
-log_info "Bundle: $BUNDLE_PATH"
-log_info ""
-
-# Step 1: Verify bundle can be extracted
-log_info "=== Step 1: Extract bundle ==="
-if ! tar -tzf "$BUNDLE_PATH" > /dev/null 2>&1; then
- log_error "Bundle is not a valid tar.gz archive"
- if [[ "$JSON_OUTPUT" == "true" ]]; then
- echo "{\"valid\": false, \"errors\": [\"Bundle is not a valid tar.gz archive\"]}"
- fi
- exit 1
-fi
-
-tar -xzf "$BUNDLE_PATH" -C "$WORK_DIR"
-log_info "Bundle extracted successfully"
-
-# Step 2: Check required structure
-log_info ""
-log_info "=== Step 2: Verify structure ==="
-
-REQUIRED_FILES=(
- "manifests/provider-manifest.json"
-)
-
-for file in "${REQUIRED_FILES[@]}"; do
- if [[ ! -f "$WORK_DIR/$file" ]]; then
- log_error "Missing required file: $file"
- else
- log_info "Found: $file"
- fi
-done
-
-# Step 3: Parse and validate manifest
-log_info ""
-log_info "=== Step 3: Validate manifest ==="
-
-MANIFEST_FILE="$WORK_DIR/manifests/provider-manifest.json"
-if [[ -f "$MANIFEST_FILE" ]]; then
- # Extract manifest fields using basic parsing (portable)
- if command -v jq &> /dev/null; then
- BUNDLE_ID=$(jq -r '.bundleId // empty' "$MANIFEST_FILE")
- BUNDLE_VERSION=$(jq -r '.version // empty' "$MANIFEST_FILE")
- INPUTS_HASH=$(jq -r '.inputsHash // empty' "$MANIFEST_FILE")
- PROVIDER_COUNT=$(jq '.providers | length' "$MANIFEST_FILE")
-
- log_info "Bundle ID: $BUNDLE_ID"
- log_info "Version: $BUNDLE_VERSION"
- log_info "Inputs Hash: $INPUTS_HASH"
- log_info "Provider count: $PROVIDER_COUNT"
- else
- # Fallback to grep-based parsing
- BUNDLE_ID=$(grep -o '"bundleId"[[:space:]]*:[[:space:]]*"[^"]*"' "$MANIFEST_FILE" | cut -d'"' -f4 || echo "")
- log_info "Bundle ID: $BUNDLE_ID (jq not available - limited parsing)"
- fi
-
- # Validate required fields
- if [[ -z "$BUNDLE_ID" ]]; then
- log_error "Manifest missing bundleId"
- fi
-else
- log_error "Manifest file not found"
-fi
-
-# Step 4: Verify provider files
-log_info ""
-log_info "=== Step 4: Verify provider files ==="
-
-# Check for mandatory provider (cisa-kev)
-CISA_KEV_FILE="$WORK_DIR/providers/cisa-kev/snapshot"
-if [[ -f "$CISA_KEV_FILE" ]]; then
- log_info "Found mandatory provider: cisa-kev"
- MANDATORY_FOUND=true
-
- # Verify hash if jq is available
- if command -v jq &> /dev/null && [[ -f "$MANIFEST_FILE" ]]; then
- EXPECTED_HASH=$(jq -r '.providers[] | select(.providerId == "cisa-kev") | .digest' "$MANIFEST_FILE" | sed 's/sha256://')
- ACTUAL_HASH=$(sha256sum "$CISA_KEV_FILE" | cut -d' ' -f1)
-
- if [[ "$EXPECTED_HASH" == "$ACTUAL_HASH" ]]; then
- log_info " Hash verified: $ACTUAL_HASH"
- else
- log_error "cisa-kev hash mismatch: expected $EXPECTED_HASH, got $ACTUAL_HASH"
- fi
- fi
-else
- log_error "Missing mandatory provider: cisa-kev"
-fi
-
-# Check optional providers
-EPSS_FILE="$WORK_DIR/providers/first-epss/snapshot"
-if [[ -f "$EPSS_FILE" ]]; then
- log_info "Found optional provider: first-epss"
-
- if command -v jq &> /dev/null && [[ -f "$MANIFEST_FILE" ]]; then
- EXPECTED_HASH=$(jq -r '.providers[] | select(.providerId == "first-epss") | .digest' "$MANIFEST_FILE" | sed 's/sha256://')
- ACTUAL_HASH=$(sha256sum "$EPSS_FILE" | cut -d' ' -f1)
-
- if [[ "$EXPECTED_HASH" == "$ACTUAL_HASH" ]]; then
- log_info " Hash verified: $ACTUAL_HASH"
- else
- log_error "first-epss hash mismatch: expected $EXPECTED_HASH, got $ACTUAL_HASH"
- fi
- fi
-else
- log_warning "Optional provider not found: first-epss"
-fi
-
-OSV_FILE="$WORK_DIR/providers/osv/snapshot"
-if [[ -f "$OSV_FILE" ]]; then
- log_info "Found optional provider: osv"
-else
- log_warning "Optional provider not found: osv (this is OK unless --include-osv was specified)"
-fi
-
-# Step 5: Verify DSSE signature (if present)
-log_info ""
-log_info "=== Step 5: Check signatures ==="
-
-DSSE_FILE="$WORK_DIR/signatures/provider-manifest.dsse"
-if [[ -f "$DSSE_FILE" ]]; then
- log_info "Found manifest DSSE signature"
-
- # Basic DSSE structure check
- if command -v jq &> /dev/null; then
- PAYLOAD_TYPE=$(jq -r '.payloadType // empty' "$DSSE_FILE")
- SIG_COUNT=$(jq '.signatures | length' "$DSSE_FILE")
-
- if [[ "$PAYLOAD_TYPE" == "application/vnd.stellaops.risk-bundle.manifest+json" ]]; then
- log_info " Payload type: $PAYLOAD_TYPE (valid)"
- else
- log_warning "Unexpected payload type: $PAYLOAD_TYPE"
- fi
-
- log_info " Signature count: $SIG_COUNT"
- fi
-else
- log_warning "No DSSE signature found"
-fi
-
-# Check detached bundle signature
-if [[ -n "$SIGNATURE_PATH" ]]; then
- if [[ -f "$SIGNATURE_PATH" ]]; then
- log_info "Found detached bundle signature: $SIGNATURE_PATH"
- # TODO: Implement actual signature verification
- else
- log_error "Specified signature file not found: $SIGNATURE_PATH"
- fi
-fi
-
-# Step 6: Summarize results
-log_info ""
-log_info "=== Verification Summary ==="
-
-ERROR_COUNT=${#ERRORS[@]}
-WARNING_COUNT=${#WARNINGS[@]}
-
-if [[ "$JSON_OUTPUT" == "true" ]]; then
- # Output JSON result
- ERRORS_JSON=$(printf '%s\n' "${ERRORS[@]}" | jq -R . | jq -s . 2>/dev/null || echo "[]")
- WARNINGS_JSON=$(printf '%s\n' "${WARNINGS[@]}" | jq -R . | jq -s . 2>/dev/null || echo "[]")
-
- cat <&1
-
- if ($LASTEXITCODE -eq 0) {
- $added++
- } else {
- Write-Host " Failed: $result" -ForegroundColor Yellow
- $failed++
- }
-}
-
-Write-Host ""
-Write-Host "=== Summary ==="
-Write-Host "Added: $added"
-Write-Host "Failed: $failed"
-Write-Host "Total: $($testProjects.Count)"
diff --git a/devops/scripts/add-testkit-reference.py b/devops/scripts/add-testkit-reference.py
deleted file mode 100644
index 038dbfced..000000000
--- a/devops/scripts/add-testkit-reference.py
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/usr/bin/env python3
-"""
-Adds StellaOps.TestKit ProjectReference to test projects that use TestCategories
-but are missing the reference.
-"""
-
-import os
-import re
-import sys
-from pathlib import Path
-
-
-def get_relative_path_to_testkit(csproj_path: Path) -> str:
- """Calculate relative path from csproj to TestKit project."""
- # TestKit is at src/__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj
- csproj_dir = csproj_path.parent
- src_root = None
-
- # Walk up to find src directory
- current = csproj_dir
- depth = 0
- while current.name != 'src' and depth < 10:
- current = current.parent
- depth += 1
-
- if current.name == 'src':
- src_root = current
- else:
- return None
-
- # Calculate relative path from csproj to src/__Libraries/StellaOps.TestKit
- rel_path = os.path.relpath(
- src_root / '__Libraries' / 'StellaOps.TestKit' / 'StellaOps.TestKit.csproj',
- csproj_dir
- )
- # Normalize to forward slashes for XML
- return rel_path.replace('\\', '/')
-
-
-def project_uses_testkit(csproj_dir: Path) -> bool:
- """Check if any .cs file in the project directory uses TestCategories."""
- for cs_file in csproj_dir.rglob('*.cs'):
- if '/obj/' in str(cs_file) or '/bin/' in str(cs_file):
- continue
- try:
- content = cs_file.read_text(encoding='utf-8-sig', errors='ignore')
- if 'TestCategories.' in content:
- return True
- except:
- pass
- return False
-
-
-def project_has_testkit_reference(content: str) -> bool:
- """Check if csproj already references TestKit."""
- return 'StellaOps.TestKit' in content
-
-
-def add_testkit_reference(csproj_path: Path, dry_run: bool = False) -> bool:
- """Add TestKit reference to csproj if needed."""
- try:
- content = csproj_path.read_text(encoding='utf-8')
- except Exception as e:
- print(f" Error reading {csproj_path}: {e}", file=sys.stderr)
- return False
-
- if project_has_testkit_reference(content):
- return False
-
- if not project_uses_testkit(csproj_path.parent):
- return False
-
- rel_path = get_relative_path_to_testkit(csproj_path)
- if not rel_path:
- print(f" Could not determine path to TestKit from {csproj_path}", file=sys.stderr)
- return False
-
- # Find a good place to insert the reference - look for existing ProjectReference
- if ' that contains ProjectReference
- pattern = r'( ]+/>\s*\n)( )'
- replacement = f'\\1 \n\\2'
- fixed = re.sub(pattern, replacement, content, count=1)
- else:
- # No ProjectReference, add a new ItemGroup before
- pattern = r'(