Installation Guide
Audience: developers and contributors bringing Stella Ops up from a source checkout with Docker Compose.
Which install path do you want?
If you are evaluating or deploying Stella Ops, this is the wrong page.
The supported install path is the release bundle: published images, a single
docker-compose.yml, one installer script, no source checkout and no build. Its documentation isdevops/release/bundle/README.md, which ships inside the bundle and is the authoritative operator-facing install text.Building from source is not an offered install path. Stella Ops remains source-available under BUSL-1.1 and everything needed to build it is in this repository — but a source build is a contributor workflow, not a deployment method, and it is not what a customer install looks like. This page documents the contributor workflow.
The release version is
v1.0.0-RC1— the same string migration092_PlatformVersionSetting.sqlseeds as the installation-widePlatformVersionsetting and the console sidebar renders.⚠ Release availability — measured 2026-07-28
The
v1.0.0-RC1images are not in the registry yet. This is the one place that caveat is stated; the bundle instructions themselves are correct and testable, and will work unchanged the moment the images land.Probed
registry.stella-ops.orgdirectly over the OCI Distribution API. The registry is up and anonymous-readable (GET /v2/→200 {}, noWWW-Authenticate), so this is a complete view, not an auth-gated partial one:
GET /v2/_catalog?n=1000→{"repositories":["oci-attach","stella-cli","stella-ops-api"]}- No
stellaops/namespace. Every product image path returns HTTP 404NAME_UNKNOWN— checkedstellaops/{advisory-ai-web,router-gateway,authority,scanner-web,vexlens-web}.- No
v1.0.0-RC1tag on any repository. Bothv1.0.0-RC1and1.0.0-RC1return 404 on all three repos.- The three repositories that do exist are prior artefacts:
stella-cli(dev,v1.0,v1.1),oci-attach(dev), andstella-ops-api(68 date-stamped tags, newest2025101219, pluslatest). The newest content in the registry predates this release by roughly nine months.Consistent with that,
release-manifest.yamlcarriesdigest: nullfor all 48 product images (TODO(verify): unresolved — image was not pushed); only the four third-party infrastructure images resolve. Itssignature:block is null, so the bundle is unsigned.A second, independent issue the same probe surfaced: eight bundle files no longer match their recorded checksums, and
install.shfails closed on that (die "N bundle file(s) do not match release-manifest.yaml"), so a run would abort during verification before reaching the pull.Both clear with one action:
./tools/build-and-publish.sh --version v1.0.0-RC1 --push --sign(withCOSIGN_KEYset). It pushes the images, then pipes the resolved digests throughtools/write-manifest.py, which rewrites the manifest with real digests and a freshly hashedbundle:block — so the checksum issue clears as a side effect. It then generatesdocker-compose.pinned.ymland cosign-signs the manifest.Until then, the from-source path below is the only way to reach a running console. Tracking:
devops/release/HANDOFF-FROM-RELEASE-FINISHER.md.
The rest of this guide covers the from-source paths — an automated quick path (setup scripts that do everything end to end) and a manual step-by-step path — plus air-gapped installs, regional compliance overlays, and connector credential setup. For first-run workflows after install, continue to the Quickstart; for fully offline installs, see the Offline Kit guide.
Prerequisites
- Docker Engine with Compose v2 (
docker compose version) - Enough disk for container images plus scan artifacts (SBOMs, logs, caches)
- For production-style installs, plan for persistent volumes (PostgreSQL + object storage) and a secrets provider
ARM64 .NET runtime baseline
For ARM64 deployments, Stella Ops .NET service images must use Debian/Ubuntu glibc-based .NET SDK and ASP.NET runtime images. The ARM64 readiness baseline is linux-arm64 on glibc, not Alpine/musl.
Keep Alpine/musl out of .NET service release images unless a separate dependency audit proves every native NuGet asset and configured runtime provider library supports musl ARM64. See devops/compose/README.md for the compose image policy.
Runtime data assets (read before first deploy)
Stella Ops services depend on runtime data assets that are not produced by dotnet publish— ML model weights for semantic search, JDK/Ghidra for binary analysis, certificates, and more. Without them, services start but operate in degraded mode.
# Download and verify all runtime assets
./devops/runtime-assets/acquire.sh --all
# Or just the embedding model (required for semantic search)
./devops/runtime-assets/acquire.sh --models
# Verify existing assets
./devops/runtime-assets/acquire.sh --verify
See devops/runtime-assets/README.md for the complete inventory, Docker volume mount instructions, and air-gap packaging.
Concelier mirror seed import/export
Local and air-gapped Stella Ops Mirror setups should use the Concelier mirror seed before source schedulers start. The seed avoids re-downloading the full public advisory, distro, raw-document, and VEX bootstrap set from upstream providers.
The repository tracks seed cards, tooling, and the full initial seed payload under devops/mirror/seeds/. The multi-GB payload chunks are Git LFS objects, so run git lfs pull before a first-run import. The current card is devops/mirror/seeds/20260607T003352Z/seed-card.json, and the repository payload root is:
devops\mirror\seeds\20260607T003352Z\stellaops-mirror-seed-v1
Prepare the import override after the seed payload is restored:
.\devops\mirror\prepare-concelier-seed-import.ps1 `
-SeedArchiveRoot "$PWD\devops\mirror\seeds\20260607T003352Z\stellaops-mirror-seed-v1"
For a local compose install, start Concelier with the generated override so Mirror:SeedImport runs before Concelier source schedulers:
docker compose -f devops/compose/docker-compose.stella-ops.yml `
-f "devops/mirror/generated/docker-compose.concelier-seed-import.override.yml" `
up -d concelier
For split compose runs, use the same override with docker-compose.stella-infra.yml and docker-compose.stella-services.yml. Keep CONCELIER_MIRROR__SEEDIMPORT__SKIPWHENTARGETNOTEMPTY=true for normal restarts. Use ReplaceExisting=true only for a controlled clean replay test.
To export a fresh seed from a live local mirror database:
.\devops\mirror\export-concelier-seed.ps1 -SkipVexHubProjection
Use -AllowCheckpointedActiveJobs only when a checkpointed export is intentional and the manifest should explicitly record active work. Normal setup seeds should be quiescent.
To verify the import mechanics without duplicating a multi-GB live mirror database, run the bounded clean-import E2E:
.\devops\mirror\test-concelier-seed-import-e2e.ps1
The default compose Concelier service is sized for export work at 4 GB memory and 2 CPU. For bounded full seeded JSON, Trivy DB, or federation stress evidence runs, add devops/compose/docker-compose.concelier-export-mem.override.yml to raise Concelier to 8 GB memory and 2 CPU without changing the baseline service defaults.
When enough disk is available and the unified issue projection backfill has run, export again without the disk-safe projection skip so the seed becomes the complete Stella Ops Mirror setup skeleton.
Quick path (automated setup scripts)
The fastest way to get running. The setup scripts validate prerequisites, configure the environment, start infrastructure, build solutions, build Docker images, and launch the full platform.
Windows (PowerShell 7):
.\tools\scripts\setup.ps1 # full setup
.\tools\scripts\setup.ps1 -InfraOnly # infrastructure only (PostgreSQL, Valkey, RustFS, Rekor, Zot)
.\tools\scripts\setup.ps1 -QaIntegrationFixtures # full setup plus Harbor/GitHub App QA fixtures
Linux / macOS:
./tools/scripts/setup.sh # full setup
./tools/scripts/setup.sh --infra-only # infrastructure only
./tools/scripts/setup.sh --qa-integration-fixtures # full setup plus Harbor/GitHub App QA fixtures
The scripts will:
- Check prerequisites (dotnet 10.x, node 20+, docker, git)
- Offer to install hosts file entries automatically
- Generate an ignored local
.envwith per-machine secrets if needed - Start infrastructure and wait for healthy containers
- Let Docker Compose create or reuse the platform bridge networks (
stellaopsandFRONTDOOR_NETWORK, defaultstellaops_frontdoor) - Stop repo-local host-run Stella services that would lock build outputs, then build repo-owned .NET solutions and publish backend services locally into small Docker contexts before building hardened runtime images (vendored or generated trees such as
node_modules,dist,coverage, andoutputare excluded) - Launch the full platform with health checks, perform one bounded restart pass for services that stay unhealthy after first boot, wait for the first-user frontdoor bootstrap path (
/signin,/envsettings.json, OIDC discovery,/connect/authorize), then complete an authenticated convergence gate that proves topology inventory, notifications administration overrides, and promotion bootstrap flows load cleanly before reporting success - If
-QaIntegrationFixtures/--qa-integration-fixturesis enabled, start deterministic Harbor and GitHub App fixtures and verify them so the local Integrations Hub can be exercised with successful UI onboarding
Open https://stella-ops.local when setup completes.
The automated setup path does not start the real third-party integration compose lane. devops/compose/docker-compose.testing.yml is the CI/testing lane, and the optional real providers live in devops/compose/docker-compose.integrations.yml. GitLab and Consul are opt-in there because they add noticeable idle CPU overhead.
Setup failures after prerequisites, migrations, first-user bootstrap, OIDC discovery, or authenticated readiness are product or documentation defects. Fix the failing script, code path, migration, or documentation and rerun setup. Do not bypass failed gates with seeded tokens, direct SQL edits, skipped checks, or unrelated volume wipes unless this guide explicitly calls for a clean-volume test.
For local compose/source-checkout installs where the existing admin password is stale or unknown, reset it through the compose Authority recovery helper:
$env:STELLAOPS_ADMIN_PASS = '<new-private-password>'
$env:STELLAOPS_ADMIN_PASS |
.\devops\compose\scripts\authority-password-set.ps1 -Username admin -Tenant default -PasswordStdin
Then update the ignored local env value used by the stack and refresh browser or CLI sessions. This is existing-admin recovery; it is separate from setup-session reset and from fresh first-user bootstrap.
For targeted backend rebuilds after a scoped code change on Windows:
.\devops\docker\build-all.ps1 -Services notify-web,orchestrator
This path avoids re-sending the full monorepo to Docker for every .NET service image.
Manual path (step by step)
1. Environment file
cd devops/compose
./scripts/init-local-env.sh
The script uses env/stellaops.env.local-dev.example and writes an ignored .env file with generated secrets. For production or release installs, start from env/stellaops.env.example, replace every CHANGE_ME value, and source real secrets from the approved secret store.
2. Hosts file
Stella Ops services bind to unique loopback IPs so all can use port 443 without collisions. Add the entries from devops/compose/hosts.stellaops.local to your hosts file:
Runtime URL convention remains *.stella-ops.local; hosts.stellaops.local is the template file name only.
The same template also carries the optional harbor-fixture.stella-ops.local and github-app-fixture.stella-ops.local aliases used by the fixture-backed integrations QA lane.
- Windows:
C:\Windows\System32\drivers\etc\hosts(run editor as Administrator) - Linux / macOS:
sudo sh -c 'cat devops/compose/hosts.stellaops.local >> /etc/hosts'
3. Start infrastructure
cd devops/compose
docker compose -f docker-compose.dev.yml up -d
docker compose -f docker-compose.dev.yml ps # verify all healthy
4. Start the full platform
The full platform Compose file owns the stellaops and frontdoor bridge networks on local installs. Do not pre-create FRONTDOOR_NETWORK; doing so without Compose labels can make docker compose up fail on a fresh setup.
docker compose --env-file .env -f docker-compose.stella-ops.yml up -d
Optional overlays:
# With Sigstore transparency log
docker compose -f docker-compose.stella-ops.yml --profile sigstore up -d
# With telemetry stack (Prometheus, Tempo, Loki)
docker compose -f docker-compose.stella-ops.yml -f docker-compose.telemetry.yml up -d
4a. Migration preflight and execution
Run a migration preflight after bringing up the stack:
# Check migration status for currently registered CLI modules
stella system migrations-status --module all
# Validate checksums for currently registered CLI modules
stella system migrations-verify --module all
# Optional: preview release migrations before any execution
stella system migrations-run --module all --category release --dry-run
If release migrations must be executed:
stella system migrations-run --module all --category release --force
stella system migrations-status --module all
Canonical policy for upgradeable on-prem installs:
- Use this CLI sequence as the required migration gate before rollouts and cutovers.
- Do not rely on Postgres init scripts for release upgrades.
- Use
docs/db/MIGRATION_CONSOLIDATION_PLAN.mdanddocs/db/MIGRATION_INVENTORY.mdto confirm module coverage and cutover wave state. - On empty migration history, CLI/API paths synthesize one per-service consolidated migration (
100_consolidated_<service>.sql) and then backfill legacy migration history rows to preserve incremental upgrade compatibility. - If consolidated history exists with partial legacy backfill, CLI/API paths auto-backfill missing legacy rows before source-set execution.
- UI-driven migration operations must call Platform WebService admin endpoints (
/api/v1/admin/migrations/*) withplatform.setup.admin; do not connect the browser directly to PostgreSQL. - Platform migration API implementation is in
src/Platform/StellaOps.Platform.WebService/Endpoints/MigrationAdminEndpoints.csand usessrc/Platform/__Libraries/StellaOps.Platform.Database/MigrationModuleRegistry.cs.
Notes:
- Compose PostgreSQL bootstrap scripts in
devops/compose/postgres-initrun only on first database initialization. devops/compose/postgres-init/14-platform-environment-settings.sqlnow leavesplatform.environment_settingsempty on fresh local databases so the setup wizard owns first-run completion truth. Older local volumes with the legacy(tenant_id, key)table shape are converged by Platform release migration064_EnvironmentSettingsInstallationScopeConvergence.sql.- Startup-hosted migrations are currently wired only for selected modules; CLI coverage is also module-limited.
- For the authoritative current-state module matrix, use
docs/db/MIGRATION_INVENTORY.md.
5. Verify
docker compose -f docker-compose.stella-ops.yml ps
curl -k https://stella-ops.local # should return the Angular UI
After the Angular UI is reachable, the supported local operator lanes are:
Browser-driven operator lane
Use the live browser UI at https://stella-ops.local and open the Integrations Hub at /setup/integrations.
For a repeatable browser-driven run against the live frontdoor:
node src/Web/StellaOps.Web/scripts/live-integrations-ui-bootstrap.mjs
This harness signs in through the same frontdoor flow, drives the /setup/integrations/onboarding/* routes in a real browser, and writes evidence to src/Web/StellaOps.Web/output/playwright/live-integrations-ui-bootstrap.json.
For a repeatable browser-driven proof of the setup wizard’s truthful state model:
node src/Web/StellaOps.Web/scripts/live-setup-wizard-state-truth-check.mjs
This harness signs in through the frontdoor, forces a fresh installation-scoped setup session, proves that database probe does not complete the step, proves that apply advances the backend state to the cache step, and proves that a page reload resumes the same persisted session. Evidence is written to src/Web/StellaOps.Web/output/playwright/live-setup-wizard-state-truth-check.json.
For a fresh-volume first-run bootstrap proof that drives the full installation wizard, creates the first administrator account, and then proves that the new credentials can authenticate through the frontdoor:
npm --prefix src/Web/StellaOps.Web run test:e2e:platform:bootstrap:first-run:live
Evidence is written to src/Web/StellaOps.Web/output/playwright/live-setup-wizard-first-run-bootstrap.json plus the paired authentication report/state files in the same directory.
Verified current UI boundary on 2026-04-14:
- The browser flow can create the full 16-entry local integration catalog.
- GitLab-class providers can now be created from the UI without a manual Vault write because the Integrations Hub stages credentials through Secret Authority before binding the returned
authref://.... - The setup wizard now persists authoritative installation-scoped progress in
platform.setup_sessionsand owns only the five control-plane steps the running control plane can truthfully converge: PostgreSQL, Valkey, schema migrations, admin bootstrap, and crypto profile. - The Admin step depends on Platform reaching Authority’s internal bootstrap endpoint with the shared bootstrap API key. In local compose, this is wired by forwarding
AUTHORITY_BOOTSTRAP_APIKEYinto Platform asSTELLAOPS_BOOTSTRAP_KEY. - For dev/CI bypass paths (runtime default admin bootstrap, env-gated demo seed migration, driver-script
setup), seedocs/modules/authority/operations/bootstrap-modes.md. Production deployments must use the setup wizard described above. Demo seed mode is sample-data opt-in and is not appropriate for prod. - Tenant-scoped onboarding stays on
/setup/*and other authenticated module surfaces instead of being duplicated inside the bootstrap wizard. - The CLI now owns the repeatable local GitLab path, including PAT minting and Vault staging, through
stella config integrations bootstrap local. - The Playwright harness still accepts
STELLAOPS_UI_BOOTSTRAP_GITLAB_ACCESS_TOKENandSTELLAOPS_UI_BOOTSTRAP_GITLAB_REGISTRY_BASICwhen you want the browser flow to use explicit operator-provided values instead of the CLI-owned local bootstrap lane.
CLI convergence lane
For a fresh local developer install, populate the live integration catalog with:
stella auth login
stella config integrations bootstrap local
This converges the default local-ready lane to 13 healthy providers: Harbor fixture, Docker Registry, Nexus, GitHub App fixture, Gitea, Jenkins, Vault, Consul, eBPF runtime-host fixture, MinIO, and the three feed mirror providers (StellaOpsMirror, NvdMirror, OsvMirror).
GitLab server/CI and the GitLab registry remain opt-in but stay inside the same first-party CLI workflow:
stella config integrations bootstrap local --include-gitlab
stella config integrations bootstrap local --include-gitlab --include-gitlab-registry
The CLI mints the owned local stella-local-integration PAT against the compose fixture, stages both authref://vault/gitlab#access-token and authref://vault/gitlab#registry-basic into the writable Vault target, and then verifies the resulting integrations. Start the heavy GitLab profile with GITLAB_ENABLE_REGISTRY=true before using --include-gitlab-registry. The older PowerShell scripts remain available for compatibility and debugging, but they are no longer the preferred product path for the Stella-owned local fixture lane.
Air-gapped deployments
For offline/air-gapped environments, use the sealed CI compose file and offline telemetry overlay:
# Sealed CI environment (authority, signer, attestor in isolation)
docker compose -f docker-compose.sealed-ci.yml up -d
# Offline observability (no external endpoints)
docker compose -f docker-compose.stella-ops.yml -f docker-compose.telemetry-offline.yml up -d
# Tile proxy for air-gapped Sigstore verification
docker compose -f docker-compose.stella-ops.yml -f docker-compose.tile-proxy.yml up -d
For offline bundles, imports, and update workflows, see:
docs/OFFLINE_KIT.mddocs/modules/airgap/guides/overview.md
Regional compliance overlays
| Region | Testing | Production |
|---|---|---|
| China (SM2/SM3/SM4) | docker-compose.compliance-china.yml + docker-compose.crypto-provider.crypto-sim.yml | docker-compose.compliance-china.yml + docker-compose.crypto-provider.smremote.yml |
| Russia (GOST) | docker-compose.compliance-russia.yml + docker-compose.crypto-provider.crypto-sim.yml | docker-compose.compliance-russia.yml + docker-compose.crypto-provider.cryptopro.yml |
| EU (eIDAS) | docker-compose.compliance-eu.yml + docker-compose.crypto-provider.crypto-sim.yml | docker-compose.compliance-eu.yml |
ARM64 note: Stella Ops first-party .NET service images are ARM64-ready on Debian/Ubuntu glibc linux-arm64. Local Russian vendor GOST providers are not supported on ARM64 installations in this release unless the operator obtains a provider-supported Debian/Ubuntu linux-arm64 package and route evidence. If you require that path, contact your GOST provider for ARM64 Linux support. Where certification rules allow substitution, use an open-source OpenSSL GOST or PKCS#11 route.
See devops/compose/README.md for detailed compliance deployment instructions.
Operator-managed trust anchors (CA certificates)
Some upstream advisory feeds (Apple’s gdmf.apple.com, regional CERTs, internal stella-mirror replicas) chain to roots that ship neither in the Ubuntu ca-certificates package nor in your corporate trust bundle. Drop PEM-encoded CAs into etc/trust-anchors/ and Concelier (plus any other service that goes through Connector.Common’s AddSourceHttpClient) will pick them up at startup — no per-connector embed, no rebuilt image.
- Mount path inside containers:
/etc/stellaops/trust-anchors(override via env varSTELLAOPS_TRUST_ANCHORS_PATH). - See
docs/modules/concelier/operations/trust-anchors.mdfor file format, startup logging, and dev-mode fallback semantics. - The mount is wired into
concelier,excititor-web,excititor-worker, andadvisory-ai-webindevops/compose/docker-compose.stella-services.yml. Add the samevolumes:+STELLAOPS_TRUST_ANCHORS_PATHenv to any other service that needs to trust a private CA.
Connector credentials (vendor advisory feeds)
Concelier ingests vulnerability advisories from many sources. The four auth-gated ones — cisco, microsoft (MSRC), ghsa, cve (CVE.org) — require operator-supplied credentials. Without credentials those connectors log a single WARN on the first cron tick and then no-op silently; they do not fail health checks or accumulate error_count. Wire them when ready.
How to obtain a PAT / OAuth client / API key for each, the exact environment variables to set, and a copy/paste verification command:
Companion references:
credentials-matrix.md— full per-source mechanism inventory (PAT / OAuth / API key / public).- The
stella-mirrorconnector is disabled by default as of Sprint 20260505_044. It is a downstream relay for air-gap operators who pull a pre-aggregated bundle from a parent Stella Ops deployment; it is not a feed Stella Ops itself consumes from upstream. Activate by settingSTELLA_MIRROR_BASE_URLto the parent’s mirror root and flippingenabled=trueinvuln.sources(or via the UI).
Hardening: require Authority for Concelier job triggers
If Concelier is exposed to untrusted networks, require Authority-issued tokens for /jobs* endpoints:
CONCELIER_AUTHORITY__ENABLED=true
CONCELIER_AUTHORITY__ALLOWANONYMOUSFALLBACK=false
Store the client secret outside source control (Docker secrets, mounted file, or Kubernetes Secret). For audit fields and alerting guidance, see docs/modules/concelier/operations/authority-audit-runbook.md.
Next steps
- Quickstart:
docs/quickstart.md - Developer setup details:
docs/dev/DEV_ENVIRONMENT_SETUP.md - Architecture overview:
docs/ARCHITECTURE_OVERVIEW.md - Compose profiles reference:
devops/compose/README.md
