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 is devops/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 migration 092_PlatformVersionSetting.sql seeds as the installation-wide PlatformVersion setting and the console sidebar renders.

⚠ Release availability — measured 2026-07-28

The v1.0.0-RC1 images 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.org directly over the OCI Distribution API. The registry is up and anonymous-readable (GET /v2/200 {}, no WWW-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 404 NAME_UNKNOWN — checked stellaops/{advisory-ai-web,router-gateway,authority,scanner-web,vexlens-web}.
  • No v1.0.0-RC1 tag on any repository. Both v1.0.0-RC1 and 1.0.0-RC1 return 404 on all three repos.
  • The three repositories that do exist are prior artefacts: stella-cli (dev, v1.0, v1.1), oci-attach (dev), and stella-ops-api (68 date-stamped tags, newest 2025101219, plus latest). The newest content in the registry predates this release by roughly nine months.

Consistent with that, release-manifest.yaml carries digest: null for all 48 product images (TODO(verify): unresolved — image was not pushed); only the four third-party infrastructure images resolve. Its signature: 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.sh fails 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 (with COSIGN_KEY set). It pushes the images, then pipes the resolved digests through tools/write-manifest.py, which rewrites the manifest with real digests and a freshly hashed bundle: block — so the checksum issue clears as a side effect. It then generates docker-compose.pinned.yml and 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

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:

  1. Check prerequisites (dotnet 10.x, node 20+, docker, git)
  2. Offer to install hosts file entries automatically
  3. Generate an ignored local .env with per-machine secrets if needed
  4. Start infrastructure and wait for healthy containers
  5. Let Docker Compose create or reuse the platform bridge networks (stellaops and FRONTDOOR_NETWORK, default stellaops_frontdoor)
  6. 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, and output are excluded)
  7. 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
  8. If -QaIntegrationFixtures / --qa-integration-fixtures is 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.

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:

Notes:

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:

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:

Regional compliance overlays

RegionTestingProduction
China (SM2/SM3/SM4)docker-compose.compliance-china.yml + docker-compose.crypto-provider.crypto-sim.ymldocker-compose.compliance-china.yml + docker-compose.crypto-provider.smremote.yml
Russia (GOST)docker-compose.compliance-russia.yml + docker-compose.crypto-provider.crypto-sim.ymldocker-compose.compliance-russia.yml + docker-compose.crypto-provider.cryptopro.yml
EU (eIDAS)docker-compose.compliance-eu.yml + docker-compose.crypto-provider.crypto-sim.ymldocker-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.

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:

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