Stella Ops Configuration Guide
Audience: Stella Ops platform operators and deployment engineers. Purpose: Describe the consolidated etc/ configuration structure for a Stella Ops deployment — where each service’s templates live, how configuration is layered and resolved, and how to scaffold and customize active configs.
Layout note (reconcile against repo). The grouped directory tree below (and the matching one in
etc/README.md) is the intended layout thatdevops/scripts/init-config.shscaffolds. The committed repository today is a hybrid: many service templates live as flat files at theetc/root (e.g.etc/router.yaml.sample,etc/policy-engine.yaml.sample,etc/signals.yaml.sample,etc/concelier.yaml.sample,etc/vexlens.yaml.sample), and several of the per-service subdirectories thatinit-config.shcreates (etc/scanner/,etc/router/,etc/scheduler/,etc/signals/,etc/vex/,etc/policy/packs/,etc/policy/schemas/,etc/crypto/profiles/) are created empty because no sample files are placed inside them. The actual on-disk locations are called out per section below. Verified againstetc/on 2026-05-30.
Directory Structure Overview
Configuration is rooted at etc/ at the repository root. This is intended as a single source of truth for service configurations, trust anchors, crypto profiles, and plugin manifests.
The grouped layout that init-config.sh provisions (mirrored in etc/README.md):
etc/
├── authority/ # Authority keys + auth provider plugin configs
├── certificates/ # Trust anchors / router-plugin trust roots
├── crypto/ # (created by init-config.sh; profiles live in etc/rootpack/)
├── env/ # Environment-specific profiles (dev/stage/prod/airgap)
├── issuer-directory/ # Issuer directory config
├── keys/ # Sample scanner signing key material
├── llm-providers/ # AI/LLM provider configurations
├── notify/ # (created by init-config.sh; templates live in etc/notify-templates/)
├── notify-templates/ # Notification templates (YAML)
├── plugins/ # Plugin manifests (NOT binaries)
├── rootpack/ # Regional crypto profiles (cn/eu/kr/ru/us-fips)
├── scm-connectors/ # Source control integration configs
├── secrets/ # Development secrets only (NEVER production)
├── trust-anchors/ # Trust anchor material
├── trust-profiles/ # Region/scheme trust profiles (JSON)
├── weights/ # Scoring weight bundles
└── README.md # etc/ overview (same aspirational tree as above)
Many single-service templates are committed as flat files directly under etc/, not under a per-service subdirectory — for example:
etc/authority.yaml.sample etc/router.yaml.sample
etc/concelier.yaml.sample etc/scanner.poe.yaml.sample
etc/policy-engine.yaml.sample etc/scanner.vexgate.yaml.sample
etc/policy-gates.yaml.sample etc/signals.yaml.sample
etc/policy-gateway.yaml.sample etc/scm-connectors.yaml.sample
etc/excititor.worker.yaml.sample etc/vexlens.yaml.sample
etc/trust-lattice.yaml.sample etc/packs-registry.yaml.sample
Configuration Precedence
Configuration values are resolved in the following order (highest priority first):
- Command-line flags - service-specific (most services do not expose a generic
--config-key=value) - Environment variables - service-specific prefix (see below)
- Active config file -
etc/<service>.yaml(flat) oretc/<service>/<service>.yaml - Default values - Built into the application
Environment Variable Naming
There is no single STELLAOPS_<SERVICE>__<KEY> convention across all services. Each .NET service registers its own environment-variable prefix in its Program.cs via AddEnvironmentVariables(prefix: ...), and the double underscore (__) is the .NET standard separator for nested configuration. Known prefixes (verified in src/**/Program.cs):
| Service | Env prefix | Example |
|---|---|---|
| Advisory AI (WebService/Worker) | ADVISORYAI__ | ADVISORYAI__SOMEKEY__NESTED=value |
| Notifier (WebService/Worker) | NOTIFIER_ | NOTIFIER_... |
| SBOM Service | SBOM_ | SBOM_... |
| Cartographer | CARTOGRAPHER_ | CARTOGRAPHER_... |
| Timeline | TIMELINE_ | TIMELINE_... |
| Attestor Tile Proxy | TILE_PROXY__ | TILE_PROXY__... |
| Runtime Instrumentation Worker | RUNTIME_INSTRUMENTATION_ | RUNTIME_INSTRUMENTATION_... |
Router (StellaOps.Router.Config) | configurable (RouterConfigOptions.EnvironmentVariablePrefix) | — |
# .NET nested-config example (Advisory AI prefix):
# Translates to { "Some": { "Nested": "value" } } under the ADVISORYAI__ prefix
ADVISORYAI__SOME__NESTED=value
Top-level deployment knobs in the
etc/env/*.env.sampleprofiles do use theSTELLAOPS_prefix without__nesting — e.g.STELLAOPS_PROFILE,STELLAOPS_LOG_LEVEL,STELLAOPS_CRYPTO_PROFILE. These are flat process environment variables consumed by Compose and bootstrap scripts, not the per-service nested-config binding shown above.
Service Configuration
File Naming Convention
| File Pattern | Purpose |
|---|---|
<service>.yaml | Active configuration (git-ignored in production) |
<service>.yaml.sample | Documented template with all options |
<service>.<profile>.yaml | Profile-specific configuration |
Creating Active Configuration
# Copy sample to create active config (router is a flat root-level template)
cp etc/router.yaml.sample etc/router.yaml
# Edit for your environment
vi etc/router.yaml
The repo does not ship
etc/scanner/scanner.yaml.sample. Scanner-related templates areetc/scanner.poe.yaml.sampleandetc/scanner.vexgate.yaml.sampleat theetc/root. Theetc/scanner/directory is created empty byinit-config.sh. Useinit-config.sh(below) to copy every committed*.sampleto its active counterpart in one pass.
Directory Reference
etc/authority/ - Authentication & Authorization
The main Authority service template is flat at the etc/ root (etc/authority.yaml.sample), not inside etc/authority/. The etc/authority/ subdirectory holds keys, the standard plugin config, and the revocation bundle schema. All Compose-loaded Authority provider YAML lives under the single etc/authority/plugins/ root. The flat etc/authority.plugins/ directory is a legacy non-Compose fallback.
etc/authority.yaml.sample # Main authority service config (flat, ~24 KB)
etc/authority/
├── keys/
├── plugins/
│ ├── harness.yaml # Deterministic signed-plugin harness config
│ ├── ldap.yaml # LDAP/Active Directory provider config
│ ├── oidc.yaml # OpenID Connect provider config
│ ├── registry.yaml # Provider registry manifest
│ ├── saml.yaml # SAML provider config
│ └── standard.yaml # Standard (built-in) auth plugin config
└── revocation_bundle.schema.json
etc/authority.plugins/
├── ldap.yaml # Legacy non-Compose fallback
└── standard.yaml # Legacy non-Compose fallback
Authority provider manifests use active
.yamlnames rather than.sample. Base, recommended, harness, and IdP-testing Compose surfaces all read/app/etc/authority/plugins; overlays change only the executable bundle root.
Key settings (see etc/authority.yaml.sample and StellaOpsScopes.cs):
- Token signing algorithms and key rotation
- OAuth2/OIDC client registration and per-client
allowed_scopes - DPoP (Demonstrating Proof of Possession) settings
- Session management
- The canonical JWT scope catalog is
StellaOps.Auth.Abstractions.StellaOpsScopes(see the Scope Reference appendix).
etc/certificates/ - Trust Anchors & Signing
etc/certificates/
└── trust-roots/
└── router-plugins/
└── stellaops-local-plugin-root.pem # Local plugin signing root
The previously documented
globalsign.pem,russian-trusted.pem, and anetc/certificates/signing/directory withauthority-signing-2025-dev.pemare not present. Sample scanner signing key material is underetc/keys/(scanner-signing-2025.key.json.sample,scanner-signing-2025.pub.json.sample). Additional trust material lives inetc/trust-anchors/andetc/trust-profiles/(global.trustprofile.json,eu-eidas.trustprofile.json,us-fips.trustprofile.json,bg-gov.trustprofile.json).
Usage:
- Compose overlays mount
etc/certificates/trust-rootsto/etc/ssl/certs/stellaops/in containers (seedocker-compose.compliance-eu.yml) - Production signing keys should come from HSM or Vault, not this directory
- Development sample keys carry a
.samplesuffix underetc/keys/
etc/concelier.yaml.sample - Advisory Ingestion
The main Concelier template is a flat root-level file (etc/concelier.yaml.sample). There is no etc/concelier/ directory; per-source connector manifests live under etc/plugins/concelier/connectors/.
etc/concelier.yaml.sample # Main concelier config (flat)
etc/excititor-calibration.yaml.sample # Excititor calibration (flat)
etc/plugins/concelier/connectors/
├── cve.yaml ├── kev.yaml ├── osv.yaml
├── distro-debian.yaml├── nvd.yaml ├── ru-bdu.yaml
├── distro-redhat.yaml├── epss.yaml ├── ghsa.yaml
├── stellaops-mirror.yaml ├── vndr-msrc.yaml └── registry.yaml
The previously documented
etc/concelier/sources/nist-nvd.yaml.sample,github-advisory.yaml.sample,oval-debian.yaml.sample,oval-rhel.yaml.sampledo not exist. NVD isnvd.yaml, GHSA isghsa.yaml, and Debian/RHEL are thedistro-debian.yaml/distro-redhat.yamlconnector manifests above.
Key settings:
- Advisory connector endpoints and credentials (per-connector manifests)
- Merge strategy and precedence rules
- Rate limiting and retry policies
- Offline mode configuration
- Connector credentials are managed via the Console (scopes
connector:credentials:read/connector:credentials:write) and dry-run-validated viaconcelier:credentials:dry-run(seeStellaOpsScopes.cs).
etc/rootpack/ - Regional Cryptographic Profiles
The committed regional crypto profiles live in
etc/rootpack/, notetc/crypto/profiles/.init-config.shcreates an emptyetc/crypto/profiles/skeleton, but the actualcrypto.profile.yamlfiles are inetc/rootpack/<region>/. Global crypto settings are flat root-level files (etc/appsettings.crypto.<region>.yaml,etc/crypto-plugins-manifest.json), and crypto provider manifests are underetc/plugins/crypto/providers/.
etc/rootpack/
├── cn/crypto.profile.yaml # China - GM/T (SM2/SM3/SM4)
├── eu/crypto.profile.yaml # EU - eIDAS qualified signatures
├── kr/crypto.profile.yaml # Korea - KCMVP
├── ru/crypto.profile.yaml # Russia - GOST R 34.10/34.11/34.12
├── us-fips/crypto.profile.yaml # USA - FIPS 140-3
└── pq-vectors.txt # Post-quantum test vectors (single shared file)
etc/appsettings.crypto.china.yaml # Per-region global crypto settings (flat)
etc/appsettings.crypto.eu.yaml
etc/appsettings.crypto.russia.yaml
etc/appsettings.crypto.international.yaml
etc/crypto-plugins-manifest.json # Crypto plugin manifest (flat)
etc/plugins/crypto/providers/
├── bouncycastle.yaml ├── cryptopro.yaml ├── eidas.yaml
├── pq-soft.yaml └── smsoft.yaml
There is no per-region
env.sampleunder each profile and noetc/crypto/crypto.yaml.sample.pq-vectors.txtis a single file atetc/rootpack/pq-vectors.txt, not per-region.
Crypto profile structure (read the real file, e.g. etc/rootpack/us-fips/crypto.profile.yaml, for the authoritative schema — the keys below are illustrative and may not match field-for-field).
Activation (verified in devops/compose/docker-compose.compliance-*.yml):
# Via environment variable (real, consumed by compliance overlays)
export STELLAOPS_CRYPTO_PROFILE=us-fips
# Via Docker Compose compliance overlay (note: paths are under devops/compose/)
docker compose \
-f devops/compose/docker-compose.stella-ops.yml \
-f devops/compose/docker-compose.compliance-eu.yml up
Compliance overlays additionally set
STELLAOPS_CRYPTO_CONFIG_PATH(e.g./app/etc/appsettings.crypto.yaml) andSTELLAOPS_CRYPTO_MANIFEST_PATHand mountetc/appsettings.crypto.<region>.yaml+etc/crypto-plugins-manifest.jsoninto the container. The overlay file names aredocker-compose.compliance-{china,eu,russia}.yml— there are nodocker-compose.fips.yml/eidas.yml/russia.ymlfiles at the repo root.
etc/env/ - Environment Profiles
etc/env/
├── dev.env.sample # Development defaults
├── stage.env.sample # Staging environment
├── prod.env.sample # Production hardened
└── airgap.env.sample # Air-gapped deployment
Environment profile contents (excerpt from the real etc/env/dev.env.sample):
# dev.env.sample
STELLAOPS_PROFILE=dev
STELLAOPS_LOG_LEVEL=Debug
POSTGRES_HOST=localhost
POSTGRES_DB=stellaops_platform # not "stellaops_dev"
STELLAOPS_TELEMETRY_ENABLED=true
STELLAOPS_TELEMETRY_ENDPOINT=http://localhost:4317
Usage with Docker Compose:
cp etc/env/dev.env.sample .env
docker compose -f devops/compose/docker-compose.stella-ops.yml up
etc/llm-providers/ - AI/LLM Configuration
etc/llm-providers/
├── claude.yaml.sample # Anthropic Claude
├── gemini.yaml.sample # Google Gemini
├── ollama.yaml.sample # Local Ollama server
├── openai.yaml.sample # OpenAI API
├── llama-server.yaml.sample # llama.cpp server
├── dummy.yaml # Dummy/offline provider
└── registry.yaml # Provider registry manifest
Provider configuration (illustrative — read etc/llm-providers/claude.yaml.sample for the authoritative keys; the API-key environment-variable name is provider/service specific and not a global STELLAOPS_LLM_APIKEY):
# claude.yaml.sample
provider: claude
endpoint: https://api.anthropic.com
model: claude-sonnet-4-20250514
options:
maxTokens: 4096
temperature: 0.1
Offline/air-gapped deployments should use ollama.yaml.sample or llama-server.yaml.sample with local model bundles.
Notification Service & Templates
The main Notify config is a flat root-level template (etc/notify.yaml.sample) with per-environment variants (etc/notify.dev.yaml, etc/notify.stage.yaml, etc/notify.prod.yaml, etc/notify.airgap.yaml). At runtime, the shipped containers read the mounted file at /app/etc/notify/notify.yaml, so operators copy the selected template into etc/notify/notify.yaml before compose mounts etc/notify/ read-only. Notification templates are YAML, not HTML, and live in etc/notify-templates/. The etc/notify/ directory is created empty by init-config.sh. Channel and provider plugin manifests live under etc/plugins/notify/.
etc/notify.yaml.sample # Main notify config (flat)
etc/notify.{dev,stage,prod,airgap}.yaml
etc/notify/notify.yaml # Runtime-mounted copy consumed by containers
etc/notify-templates/
└── vex-decision.yaml.sample # VEX decision templates (YAML, multi-channel)
etc/plugins/notify/
├── channels/ # discord, email, inapp, inappinbox, slack, telegram, webhook
├── discord/ email/ opsgenie/ pagerduty/ slack/ teams/ telegram/ webhook/
│ # per-provider config.yaml + plugin.json
└── registry.yaml
Template structure (real format — channel-typed, Liquid/{{ ... }} placeholders, render_mode: markdown; there are no standalone *.html templates):
# etc/notify-templates/vex-decision.yaml.sample
templates:
- key: vex.decision.changed
channel_type: email
locale: en-US
render_mode: markdown
description: "Notification when VEX decision status changes"
body: |
## VEX Decision Changed: {{ vulnerability_id }}
**Product:** {{ product.name }} ({{ product.version }})
**PURL:** `{{ product.purl }}`
Templates are imported via the CLI:
stella notify template import vex-decision.yaml. The previously documentedscan-complete.html,policy-violation.html, andalert.htmldo not exist.
etc/plugins/ - Plugin Manifests
Plugin manifests define available plugins. Compiled binaries live in plugins/ at root.
The actual etc/plugins/ tree is much broader than language/OS scanner analyzers and notify channels — it also covers authority, concelier connectors, crypto providers, excititor (VEX) connectors, messaging transports, router transports, and per-module registry.yaml manifests:
etc/plugins/
├── README.md
├── authority/ # ldap.yaml, oidc.yaml, standard.yaml, registry.yaml
├── binaryindex/ # registry.yaml
├── cli/ # registry.yaml
├── concelier/connectors/ # cve, nvd, ghsa, kev, epss, osv, ru-bdu, distro-*, ...
├── crypto/ # registry.yaml + providers/{bouncycastle,cryptopro,eidas,pq-soft,smsoft}.yaml
├── excititor/ # *-csaf, oci-openvex, suse-rancher (config.yaml + plugin.json each) + registry.yaml
├── export/ # registry.yaml
├── messaging/ # inmemory, postgres, valkey (config.yaml + plugin.json) + registry.yaml
├── notify/ # channels/, per-provider dirs, registry.yaml
├── policy/ # registry.yaml
├── router/ # registry.yaml + transports/{inmemory,tcp,tls,udp,rabbitmq}/
├── scanner/ # analyzers/lang, analyzers/os, buildx, entrytrace, node, + legacy lang/ os/ shims
├── scheduler/ # registry.yaml
└── vex/ # registry.yaml
The scanner analyzers exist in two forms under etc/plugins/scanner/:
analyzers/lang/<Lang>/manifest.json(canonical, e.g.StellaOps.Scanner.Analyzers.Lang.Java/manifest.json) plus lower-caseanalyzers/lang/<lang>/{config.yaml,plugin.json}directories (dotnet, go, java, node, php, python, ruby, rust, swift, cpp).analyzers/os/<OS>/manifest.json(Apk, Dpkg, Rpm) plus lower-caseanalyzers/os/<os>/{config.yaml,plugin.json}(apk, dpkg, rpm, chocolatey, homebrew, pacman).- Legacy flat shims
scanner/lang/*.yamlandscanner/os/*.yamlalso exist.
Manifests are committed primarily as
manifest.json/plugin.json(+config.yaml), not the single<lang>.yamlschema shown in earlier drafts. Read an actualetc/plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Java/manifest.jsonfor the authoritative manifest shape rather than relying on the illustrativeid/assembly/capabilities/filePatternsexample previously documented here.
Policy Engine & Gates
Policy templates are flat root-level files, not under an etc/policy/ directory. init-config.sh creates empty etc/policy/packs/ and etc/policy/schemas/ directories, but no pack bundles or JSON Schema are committed there.
etc/policy-engine.yaml.sample # Engine configuration (flat)
etc/policy-gates.yaml.sample # Gate definitions (flat)
etc/policy-gateway.yaml.sample # Policy gateway config (flat)
etc/policy.poe.yaml.sample # Proof-of-exploit policy config (flat)
etc/score-policy.yaml.sample # Scoring policy (flat)
etc/weights/v2026-01-22.weights.json # Scoring weight bundle
There is no
etc/policy/packs/starter-day1.yaml, noenterprise.yaml.sample, and noetc/policy/schemas/policy-pack.schema.jsoncommitted. Treat those as roadmap items, not shipped artifacts.
Policy gate example (illustrative — read etc/policy-gates.yaml.sample for the authoritative gate schema):
# etc/policy-gates.yaml.sample (flat)
gates:
- id: no-critical-unfixed
name: "No Critical Unfixed Vulnerabilities"
action: block
Policy authoring/promotion is governed by the policy:* scope family (policy:write, policy:author, policy:review, policy:submit, policy:approve, policy:publish, policy:promote, policy:activate, policy:simulate, policy:run, policy:audit, policy:read, policy:edit, policy:operate) — see the Scope Reference appendix.
Container Scanning
The etc/scanner/ directory is created empty by init-config.sh. The committed scanner-related templates are flat root-level files:
etc/scanner.poe.yaml.sample # Proof-of-exploit configuration (flat)
etc/scanner.vexgate.yaml.sample # Scanner VEX gate configuration (flat)
etc/keys/scanner-signing-2025.{key,pub}.json.sample # Sample signing key material
There is no
etc/scanner/scanner.yaml.sampleand noetc/scanner/poe.yaml.sample. The illustrativescanner:YAML block previously shown here (concurrency / analyzers / sbom.formats / evidence) does not correspond to a committed sample file — do not rely on those exact keys. Scanner analyzer enablement is driven by the plugin manifests underetc/plugins/scanner/analyzers/.
Scanner access is governed by scanner:read, scanner:scan, scanner:export, scanner:write, and scanner.signed-sbom-material.write (see StellaOpsScopes.cs).
VEX Processing
The etc/vex/ directory is created empty by init-config.sh. VEX templates are flat root-level files; per-source VEX (excititor) connectors are under etc/plugins/excititor/:
etc/excititor.worker.yaml.sample # Excititor (VEX ingestion) worker config (flat)
etc/excititor-calibration.yaml.sample # Excititor calibration (flat)
etc/vexlens.yaml.sample # VEX Lens consensus computation (flat)
etc/trust-lattice.yaml.sample # Issuer/source trust configuration (flat)
etc/plugins/excititor/
├── cisco-csaf/ msrc-csaf/ oci-openvex/ oracle-csaf/
├── redhat-csaf/ suse-rancher/ ubuntu-csaf/ (config.yaml + plugin.json each)
└── registry.yaml
Trust lattice example (real schema — weighted P/C/R trust vectors, not trustLevels/precedenceRules):
# etc/trust-lattice.yaml.sample
# BaseTrust = (wP × Provenance) + (wC × Coverage) + (wR × Replayability)
defaultWeights:
provenance: 0.45
coverage: 0.35
replayability: 0.20
defaultVectors:
vendor:
provenance: 0.90
coverage: 0.85
replayability: 0.70
distro:
provenance: 0.85
coverage: 0.90
replayability: 0.70 # see file for the authoritative values
VEX access is governed by vex:read, vex:ingest, vex.admin, plus vexhub:read/vexhub:admin and aoc:verify (see StellaOpsScopes.cs).
Directories Outside etc/
plugins/ - Compiled Plugin Binaries
Runtime artifacts, not configuration. Built during CI/CD.
plugins/
├── concelier/
├── scanner/
│ ├── analyzers/
│ │ ├── lang/ # Language analyzers (.dll, .pdb)
│ │ ├── os/ # OS analyzers
│ │ └── ... (bun, ccpp, dart, deno, dotnet, elixir, golang, java, node,
│ │ php, python, ruby, rust, secrets, swift)
│ └── buildx/ # BuildX SBOM plugin
└── notify/ # discord, email, slack, teams, telegram, webhook, csaf,
# dora, enisa, ncs, opsgenie, pagerduty, inapp, inappinbox, ...
The
entrytraceplugin manifest lives underetc/plugins/scanner/entrytrace/; it is not a top-levelplugins/scanner/entrytrace/binary directory in the repo.
opt/ - Optional Vendor Packages
Customer-provided packages for specific crypto providers:
opt/
└── cryptopro/
└── downloads/ # CryptoPro CSP packages (.gitkeep + README.md placeholder)
offline/ - Air-Gap Operational Data
Runtime/offline-kit state for air-gapped deployments. The committed contents are the notifier offline kit and rule material (not the previously documented feeds/, packages/, advisory-ai/ directories):
offline/
├── notifier/ # Notify offline kit (manifest.json, DSSE manifest, templates, verify script)
└── rules/ # Offline rule material (incl. rules/secrets)
Docker Compose Integration
The base compose file is
devops/compose/docker-compose.stella-ops.yml(there is nodocker-compose.ymlat the repo root). The examples below are illustrative of the mount pattern; consult the real compose files for exact service definitions and paths (note compose files referenceetc/as../../etcrelative todevops/compose/).
Volume Mounts (illustrative)
# devops/compose/docker-compose.stella-ops.yml
services:
scanner:
volumes:
# Configuration (read-only)
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
# Plugin manifests (read-only)
- ../../etc/plugins/scanner:/app/etc/plugins/scanner:ro
# Runtime data (read-write)
- scanner-data:/var/lib/stellaops/scanner
Environment File
services:
scanner:
env_file:
- ../../.env # Copied from etc/env/dev.env.sample by init-config.sh
Crypto Profile / Compliance Overlays (verified file names)
# EU / eIDAS deployment
docker compose \
-f devops/compose/docker-compose.stella-ops.yml \
-f devops/compose/docker-compose.compliance-eu.yml up
# Russian crypto deployment
docker compose \
-f devops/compose/docker-compose.stella-ops.yml \
-f devops/compose/docker-compose.compliance-russia.yml up
# China (GM/T) deployment
docker compose \
-f devops/compose/docker-compose.stella-ops.yml \
-f devops/compose/docker-compose.compliance-china.yml up
Crypto provider overlays (orthogonal to the compliance profiles) are
docker-compose.crypto-provider.cryptopro.yml,...smremote.yml, and...crypto-sim.yml. Air-gap overlays aredocker-compose.airgap-e2e.override.ymlanddocker-compose.airgap-seed.override.yml. There are nodocker-compose.fips.yml,docker-compose.eidas.yml,docker-compose.russia.yml, ordocker-compose.airgap.ymlfiles.
Quick Start
1. Initialize Configuration
# Clone sample configs
./devops/scripts/init-config.sh dev
# This copies all .sample files to active configs for development
2. Customize for Environment
# Edit main service configs (flat root-level templates)
vi etc/authority.yaml # from etc/authority.yaml.sample
vi etc/router.yaml # from etc/router.yaml.sample
# Set environment-specific values (init-config.sh writes .env at repo root)
vi .env # from etc/env/dev.env.sample
3. Select Crypto Profile (if needed)
# For US/FIPS compliance — the env var is consumed by the compliance overlays.
# Committed profile data lives in etc/rootpack/us-fips/ (there is no per-profile env.sample).
export STELLAOPS_CRYPTO_PROFILE=us-fips
4. Start Services
docker compose up -d
Configuration Validation
Not implemented.
devops/scripts/validate-config.shanddevops/scripts/validate-policy-packs.shdo not exist in the repo. The only committed configuration helper script isdevops/scripts/init-config.sh, which scaffolds the directory tree and copies every*.sampleto its active counterpart. Treat the validation commands below as a roadmap target, not a shipped capability.
# Roadmap (not yet implemented):
# ./devops/scripts/validate-config.sh
# ./devops/scripts/validate-config.sh scanner
# ./devops/scripts/validate-policy-packs.sh
Migration from Legacy Structure
Caution: the crypto rows below are aspirational, not done.
init-config.shcreates an emptyetc/crypto/profiles/skeleton, but the committed crypto profiles still live inetc/rootpack/— theetc/rootpack/ → etc/crypto/profiles/migration has not been carried out. Likewise, policy templates remain flat at theetc/root (e.g.etc/policy-engine.yaml.sample), not underetc/policy/.
| Legacy Location | Documented Target | Status |
|---|---|---|
certificates/ | etc/certificates/ | Present (etc/certificates/trust-roots/) |
config/env/.env.* | etc/crypto/profiles/*/env.sample | NOT IMPLEMENTED (no per-profile env.sample) |
config/crypto-profiles.sample.json | etc/crypto/crypto.yaml.sample | NOT IMPLEMENTED (no etc/crypto/crypto.yaml.sample) |
policies/ | etc/policy/ | Partial (flat etc/policy-*.yaml.sample; etc/policy/ empty) |
etc/rootpack/ | etc/crypto/profiles/ | NOT DONE (etc/rootpack/ is still the live location) |
See docs-archive/operations/configuration-migration.md (archived — migration complete) for detailed migration steps.
Security Considerations
- Never commit production active configs - the intended posture is that active
*.yamlare git-ignored and only*.yaml.sampleare committed. Note, however, that theetc/**/*.yamlignore rules are added byinit-config.sh(they are not present in the repo’s base.gitignore), and the repo does commit some active.yamlfiles (e.g.etc/authority.yaml,etc/llm-providers/claude.yaml,etc/notify.dev.yaml). Runinit-config.shbefore generating real deployment configs so your active files are ignored. - Secrets via environment / secret managers - prefer per-service env vars or external secret managers; do not hardcode production secrets in
etc/. - Development secrets are clearly marked -
etc/secrets/contains only dev/sample keys (*.secret,*.secret.example,dsse-dev.signing.json) plus a README. - Provision the master key at setup, and keep production key material off plain files. Secret values (connector credentials, deployment bundles) default to the built-in floor: envelope-encrypted in each service’s PostgreSQL under an installation-wide master key (KEK), provisioned at the setup Master Key step (ADR-031 D1/D3). Operators may upgrade to an external, operator-run HashiCorp Vault (or operator-run OpenBao — the in-repo OpenBao compose profile is test-only dev/CI tooling, never shipped; ADR-031 §D5 as amended) for secret storage, and may source the master key itself from an env var (default), a file, Vault, or an HSM (
Crypto:Kek:Source). There is deliberately no cloud-managed KMS (AWS/Azure/GCP KMS) default, stub, or plugin — Stella Ops is on-prem/air-gap first. Production signing and KEK material should be HSM- or Vault-backed, never left in plain files. Install-path and lifecycle detail: the Secrets & Keys guide.
Authority Scope Reference
The canonical JWT authorization surface is StellaOps.Auth.Abstractions.StellaOpsScopes (src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOpsScopes.cs). A scope can appear in an issued token only if it is (a) declared as a const there and (b) listed in the requesting client’s authority.clients.allowed_scopes. The authority.permissions table is the Console RBAC catalog and is not consulted at token-issuance time.
Scope families currently declared (read the source for the authoritative, complete list — it is enumerated reflectively by StellaOpsScopes.All):
| Family | Example scopes |
|---|---|
| Authority (legacy) | authority.users.manage, authority.clients.manage, authority.audit.read, authority.revocation.read |
| Authority (resource) | authority:tenants.read/write, authority:users.read/write, authority:roles.read/write, authority:clients.read/write, authority:tokens.read/revoke, authority:branding.read/write |
| Concelier / Advisory | concelier.jobs.read, concelier.jobs.trigger, concelier.merge, concelier:credentials:dry-run, advisory:read/ingest, advisory-ai:view/operate/admin |
| VEX | vex:read/ingest, vex.admin, vexhub:read/admin, aoc:verify |
| Policy | policy:read/write/edit/author/review/submit/approve/publish/promote/activate/run/operate/simulate/audit |
| Findings / Vuln | findings:read/write, effective:write, vuln:view/investigate/operate/audit (vuln:read is obsolete) |
| Scanner / SBOM | scanner:read/scan/export/write, scanner.signed-sbom-material.write, sbom:read/write/attest |
| Signer / Attestor / Trust | signer:read/sign/rotate/admin, attest:read/create/admin, trust:read/write/admin |
| Graph / Reachability | graph:read/write/export/simulate/admin, signals:read/write/admin |
| Evidence / Timeline / Replay | evidence:create/read/hold, timeline:read/write/admin, replay:read/write |
| Export / Notify | export.viewer/operator/admin, notify.viewer/operator/admin/escalate |
| Orchestrator / Scheduler / Packs | orch:read/operate/quota/backfill, scheduler:read/operate/admin, packs.read/write/run/approve |
| Release | release:read/write/publish/bypass |
| Air-gap | airgap:seal/import, airgap:status:read |
| Platform / Crypto | platform.context.read/write, platform.idp.read/admin, crypto:read/admin, crypto:profile:admin, crypto:kek:read/rotate, platform:federation:read/write, platform:sar:read/erase |
| Doctor / Ops / Router | doctor:run, doctor:run:full, doctor:export, doctor:admin, doctor-scheduler:read/write, ops.health, ops-memory:read/write, router:routing:read/write |
| Integration / Connector / Registry | integration:read/write/operate, connector:credentials:read/write, registry.admin |
| UI / Misc | ui.read, ui.admin, ui.preferences.read/write, analytics.read, obs:read, obs:incident, issuer-directory:read/write/admin, script:read/write, symbols:read/write, unknowns:read/write/admin, risk-engine:read/operate, zastava:read/trigger/admin, exceptions:read/write/request/approve, operator.verify.read/admin, sm-remote:hash/encrypt/decrypt/sign/verify, stellaops.bypass |
The
taskrunner:read/operate/adminscopes are removed (the TaskRunner service was deleted; the consts are kept commented-out only for DB/migration backward compatibility).vuln:readis[Obsolete]— usevuln:view.
Related Documentation
- Secrets & Keys guide (master key, secret providers, secret-reference URLs)
- Registries & masked image references
- PostgreSQL Operations Guide
- Air-Gap Operations Runbook
- Air-Gap Deployment / Offline Kit
- Crypto Profile Reference
- Policy Engine Guide
- Deployment Version Matrix
- Container Deployment Guide
