Security Hardening Guide — Stella Ops Suite v1.0.0-RC1
Applies to: the published
stellaops-bundle-v1.0.0-RC1.tar.gzevaluation bundle, not an arbitrary checkout ofmain.Reconciled: 2026-08-04 against the retained public artifact with SHA-256
e04dc3242a97d95266fee98c5b2f8d0ad8113f88e9be1ddd4b2a8a005a4864ec. Its detached manifest signature verified with the published release key;tools/verify-bundle.py --require-signature --require-digestsverified 249/249 files and 47/47 product-image digests. The artifact’s internalrelease-manifest.yamlis authoritative for its version and image digests.Audience: operators and security engineers installing Stella Ops on a Docker Engine/Compose host. RC1 does not ship a Kubernetes deployment. This guide therefore contains no Kubernetes procedure.
See also the Security Policy, release-bundle procedure, secrets and keys guide, and backup/restore drill.
1. Security boundary and threat model
The RC1 bundle is a multi-service Compose application. It is not a single backend container. Its main security boundaries are:
| Boundary | RC1 reality | Primary control |
|---|---|---|
| Public ingress | Router Gateway publishes HTTP 8080 and HTTPS 8443 | Bind to loopback by default; put a production TLS reverse proxy/firewall in front before remote access |
| Evaluation registry | The bundled registry publishes 5000 and has no authentication in RC1 | Keep it on loopback; do not expose it to an untrusted network |
| Service traffic | Two Docker bridge networks, stellaops and frontdoor; no service port other than the three above is host-published | Host firewall, Docker network isolation, Router authorization |
| Persistence | PostgreSQL, Valkey, SeaweedFS (rustfs service), the registry, and product state use named volumes | Host/storage access control, encryption at the storage layer, tested backup and restore |
| Control credentials | Installation-specific secrets are generated into .env; private signing material is generated or supplied under installation-local paths | Restrictive file ACLs, external custody for production keys, controlled rotation |
| Supply chain | 47 product images plus four digest-pinned infrastructure images | Verify the signed manifest and deploy the digest-pinned Compose file |
| Host control | Opt-in agent-core mounts /var/run/docker.sock | Enable only on a dedicated promotion host; Docker-socket access is root-equivalent |
The bundle defaults are suitable for a loopback-bound evaluation. They are not a claim that an unchanged workstation install is production hardened.
2. Verify before installation
Work from a newly extracted bundle. Obtain the release public key over an independent channel and compare it with the copy in the archive before trusting the manifest.
sha256sum stellaops-bundle-v1.0.0-RC1.tar.gz
# expected for this reconciliation:
# e04dc3242a97d95266fee98c5b2f8d0ad8113f88e9be1ddd4b2a8a005a4864ec
tar xzf stellaops-bundle-v1.0.0-RC1.tar.gz
cd stellaops-bundle-v1.0.0-RC1
curl -fsSL https://stella-ops.org/keys/cosign.pub -o public-release-signing.pub
cmp public-release-signing.pub release-signing.pub
cosign verify-blob --insecure-ignore-tlog --key public-release-signing.pub \
--signature release-manifest.yaml.sig release-manifest.yaml
python tools/verify-bundle.py --require-signature --require-digests
RC1 uses a pinned offline release key and has no Rekor entry. The --insecure-ignore-tlog option skips only the online transparency-log lookup; it does not skip detached-signature verification.
Run ./install.sh --no-start (or ./install.ps1 -NoStart) before the first start. Then review the generated .env, key paths, certificate paths, and secrets/docker-compose.installation.yml. Start with the generated digest-pinned file when reproducibility is required:
docker compose --env-file .env \
-f docker-compose.pinned.yml \
-f secrets/docker-compose.installation.yml config --quiet
Do not use latest, substitute an unverified image, or copy a manifest from a different release.
3. Host and container runtime baseline
- Use a supported Docker Engine 24+ with Compose v2. Restrict membership of the Docker group and access to the daemon socket as privileged administration.
- Patch the host OS and Docker through the operator’s normal security channel; disable remote Docker API listeners unless mutually authenticated and firewall-restricted.
- Store the bundle and
.envon a local encrypted filesystem. On Linux, confirm.env, private keys, PFX files, and generated secret overlays are mode0600; on Windows, replace inherited/shared-folder ACLs with an ACL limited to the service operator account. - Keep Docker’s data root and all
stellaops_*volumes on encrypted storage. Do not mount them over NFS/SMB without an explicit integrity, locking, and encryption review. - Configure disk, memory, and log quotas. A host resource exhaustion event can disable policy, evidence, and authorization services together.
- Back up volumes and key material together and prove restoration with the backup/restore drill. A database restore without its historical verification keys can leave evidence unverifiable.
Compose-level controls in RC1
The published RC1 Compose file does not apply a uniform read_only, cap_drop: [ALL], security_opt: no-new-privileges, seccomp, or explicit non-root-user policy to all services. Do not claim those controls are present. Four one-shot initialization helpers explicitly run as root: advisory-ai-data-init, doctor-evidence-init, release-orchestrator-agent-ca-init, and scanner-cache-init (concelier-jobs-init left the estate compose with SPRINT_20260722_008 VULN-G3, 2026-09-08).
Apply additional Compose restrictions only through a reviewed override and validate every affected service’s health and write paths. A blanket read_only: true can break migrations, queues, caches, and key provisioning. At minimum:
- add
no-new-privilegesand capability drops service by service; - make writable paths explicit volumes or bounded
tmpfsmounts; - pin the runtime seccomp/AppArmor/SELinux policy;
- verify the container’s effective UID and mounted-file ownership; and
- retain the override beside the deployment change record, not by editing the generated
docker-compose.yml.
The agents profile is exceptional: agent-core mounts the Docker socket and must be treated as host-root code. Leave that profile disabled unless the host is intentionally a deployment target.
4. Network hardening
The RC1 artifact resolves to 56 Compose services when both opt-in profiles are included. It declares two bridge networks:
stellaops— shared internal service and data plane;frontdoor— Router-reachable API plane. Its name does not make it a host or internet-facing network.
Only these host bindings exist in the published Compose file:
| Service | Default host binding | Container port | Purpose |
|---|---|---|---|
router-gateway | 127.0.0.1:8080 | 8080/tcp | HTTP entry point/redirect |
router-gateway | 127.0.0.1:8443 | 443/tcp | Console and API over HTTPS |
registry | 127.0.0.1:5000 | 5000/tcp | Local OCI registry for evaluation scans |
postgres, valkey, and rustfs have no host-published ports. Do not add any.
Production checklist:
- Keep
STELLA_BIND_ADDRESS=127.0.0.1unless a host firewall or reverse proxy is already in place. Prefer publishing only8443through that boundary. - RC1’s bundled Zot registry has no authentication. Never set its binding to
0.0.0.0on an untrusted network. If remote registry access is required, place an authenticated registry/proxy on a separate controlled host rather than exposing RC1’s evaluation registry. - Deny direct north-south access to every container address. Route product API traffic through
router-gatewayso authorization and audit controls apply. - Default-deny host egress, then allow only operator-approved feed, registry, identity, notification, and time endpoints required by enabled features. Offline installations should have no general internet route.
- Keep the
airgapandagentsprofiles disabled unless their function and additional trust boundary have been reviewed.
5. TLS and identity
The installer creates a self-signed evaluation certificate and sets GATEWAY_ALLOW_INVALID_INTERNAL_CERTIFICATES=true. Before exposing the system:
- replace the gateway and internal service certificates with certificates issued by the operator’s CA;
- distribute only the CA/public chain to trust stores;
- set
GATEWAY_ALLOW_INVALID_INTERNAL_CERTIFICATES=false; - recreate the affected services; and
- verify sign-in, token issuance, Router forwarding, and a protected API call.
Do not publish the Authority, service certificates, or internal host aliases directly. Rotate Authority signing keys with the Authority signing-key playbook; preserve retired public keys for the required token and evidence verification window.
6. Secret ownership and rotation
The installer writes generated values to .env, generates installation-local private keys under config/etc/**/keys and secrets/**, and creates an installation-only Attestor trust overlay. No private-key header is present in the downloaded RC1 archive before installation. Public verification keys are not secrets and must remain distributable.
Environment-backed secrets
| Secret(s) | Owner | Rotation pointer |
|---|---|---|
STELLAOPS_ADMIN_PASS | Installation owner / Authority administrator | Change immediately through the Authority/Console account flow; treat the .env value as bootstrap material, not a password vault |
POSTGRES_PASSWORD and the password copy in every STELLAOPS_POSTGRES_<SERVICE>_CONNECTION | Database operator | Rotate the database role and all consumer strings in one maintenance change; validate startup migrations and service health before retiring the old credential. Enumerate the variables from the deployed compose rather than from memory — ADR-039 gave each service its own database, role and connection string, so there is one password copy per service (sixteen and counting), not the two this row named until 2026-09-14. Rotating a subset leaves the rest authenticating with a credential you believe is retired. The generic STELLAOPS_POSTGRES_CONNECTION is retired and must not be reintroduced. |
STELLAOPS_S3_ACCESS_KEY_ID, STELLAOPS_S3_SECRET_ACCESS_KEY | Object-storage operator | Rotate the SeaweedFS identity and all scanner/object-store consumers atomically; verify upload and read-back before revocation |
STELLAOPS_IDENTITY_ENVELOPE_SIGNING_KEY | Router/platform security owner | Coordinated full-stack rotation; all producers and verifiers must receive the same new key before the old key is removed |
STELLAOPS_INTEGRATIONS_CREDENTIAL_KEK, STELLAOPS_DEPLOYMENT_BUNDLE_KEK, STELLAOPS_SECRETS_ENCRYPTION_KEY | Platform cryptography owner | Follow the master KEK rotation runbook; re-seal stored ciphertext before old-key retirement |
KESTREL_CERT_PASSWORD | PKI/operator owner | Rotate with the PFX files and recreate every consumer; never change only the password or only the PFX |
CONCELIER_AUTHORITY_CLIENT_SECRET, SCHEDULER_CONCELIER_AUTHORITY_CLIENT_SECRET, SCANNER_WORKER_AUTHORITY_CLIENT_SECRET, SCANNER_POE_AUTHORITY_CLIENT_SECRET, FINDINGS_SECURITY_AUTHORITY_CLIENT_SECRET, NOTIFY__AUTHORITY__CLIENT_SECRET, ADVISORY_AI__AUTHORITY__CLIENT_SECRET, STELLAOPS_EXPORTCENTER_AUTHORITY_CLIENT_SECRET, STELLAOPS_CLI_AUTOMATION_CLIENT_SECRET, STELLAOPS_RELEASE_DISPATCH_CLIENT_SECRET, CONSOLE_WORKSPACE_CLIENT_SECRET | Named service owner plus Authority administrator | Use the Authority client-secret rotation procedure; overlap credentials where supported and prove the client flow before revoking the old secret |
RELEASE_ORCHESTRATOR_AGENT_CA_ROOT_PFX_PASSWORD_FILE (preferred) / RELEASE_ORCHESTRATOR_AGENT_CA_ROOT_PFX_PASSWORD | Release operations PKI owner | Prefer the mounted file: it wins over the inline value and an unresolvable path fails the host closed rather than falling back, so a stale mount cannot silently downgrade custody. Supplying neither refuses startup — no default ships. Rotate with the agent CA root and re-enrol affected agents; retain required public chain/history |
STELLAOPS_CONCELIER_FEDERATION_HMAC_KEY_B64 and non-secret STELLAOPS_CONCELIER_FEDERATION_HMAC_KEYID | Concelier federation owner | Coordinate every federation peer; overlap trust where supported, verify a signed exchange, then revoke the old key |
STELLAOPS_EVIDENCELOCKER_CAPSULE_ED25519_SEED_B64 | Evidence/Attestor signing owner | Follow production signing and verification-key rotation; preserve old public verification material |
INTEGRATIONS_VAULT_TOKEN | Integrations/Vault operator | RC1 generates a placeholder only. For a real Vault integration, issue a least-privilege token, replace it, restart integrations-web, prove a read, then revoke the old token |
STELLAOPS_EXPORTCENTER_NIS2_SOA_SIGNING_KEY_PEM_FILE | Compliance-export signing owner | Operator-supplied private key; the installer deliberately does not generate it. Follow production signing and publish/retain the matching public trust root |
Installer-generated file-backed private material
The installer creates the following installation-local material. Back it up in encrypted custody; never add it to source control or a rendered Compose artifact.
| Path/class | Owner | Rotation |
|---|---|---|
config/etc/authority/keys/signing-dev.pem, ack-token-dev.pem | Authority security owner | Authority signing-key playbook; keep retired public keys until expiry |
config/etc/scanner/keys/scan-attestation-dev.pem | Scanner attestation owner | Introduce the new public trust root, switch the signer, verify a scan attestation, then retire the old key |
config/etc/release-orchestrator/keys/ro-gate-decision-dev.pem | Release decision-signing owner | Rotate with downstream decision-verification trust; verify a signed gate decision |
secrets/certs/signing-dev.pem and public half | Attestor transparency/verdict owner | Verification-key rotation; retain public history needed by existing evidence |
config/etc/authority/keys/kestrel-dev.pfx, copied to secrets/certs/kestrel-dev.pfx | PKI/operator owner | Replace certificate, key, and password together; then set strict internal-certificate validation |
Operator-supplied config/etc/exportcenter/keys/nis2-soa-signing.pem | Compliance-export signing owner | Production-signing runbook; publish matching public trust before switching |
For every rotation: inventory all consumers, take an encrypted backup, rotate one secret class at a time, update all consumers atomically, recreate only the affected services, run a real authentication/sign/verify forcing function, and record the non-secret key id/fingerprint in the audit change. Do not delete old public verification material merely because the private key is retired.
7. Image, SBOM, and plugin supply-chain controls
- All 47 Stella Ops product images in RC1 are under
registry.stella-ops.org/stellaops/<service>:v1.0.0-RC1and are recorded by digest inrelease-manifest.yaml. There is no GHCR-hosted Stella Opsbackendimage. - Four infrastructure images—PostgreSQL, Valkey, SeaweedFS (
rustfs), and Zot (registry)—come from their upstream registries and are digest-pinned. An upstream registry name for those four is expected; substituting a tag is not. - Use
docker-compose.pinned.ymlto pull/deploy by digest. For an air-gapped site, mirror those exact digests and preserve the manifest/signature beside the mirror inventory. - Product plugins are carried inside the signed product images. Signature enforcement remains enabled. Out-of-band plugin bundles must be signed by a key in the operator’s configured trust root; do not disable enforcement to make an unknown bundle load.
- Keep the release manifest, detached signature, public release key, SBOMs, and installation change record together for audit.
8. Logging, monitoring, and audit
- Forward container stdout/stderr to the operator’s protected log platform; set rotation/retention limits so logs cannot exhaust the Docker host.
- Alert on Router
401/403spikes, service health failures, unexpected restarts, Authority client failures, signature-verification failures, database/storage errors, and changes to published ports or Docker networks. - Monitor Concelier authorization bypass/audit fields as described in the Concelier Authority audit runbook.
- Never log
.env, rendered Compose configuration, private keys, bearer tokens, client secrets, database URLs, or connector credentials. - Record secret rotations, trust-root changes, image digests, and the exact release manifest digest in the operator audit trail.
9. Patch, incident, and recovery workflow
Patch by building or obtaining a new signed release, verifying it in a clean directory, and promoting the same digest-pinned artifact. Do not edit the RC1 manifest or retag an image in place.
For a suspected compromise:
- contain at the host firewall/Router and disable the affected integration or service path;
- preserve logs, the signed manifest, container/image ids, audit records, and volume snapshots;
- identify the exact credential, signing key, image digest, or plugin trust root involved;
- rotate/revoke with the relevant runbook, preserving historical public verification material unless the key must be distrusted retroactively;
- redeploy a verified digest-pinned artifact and run a real protected-path or sign/verify forcing function; and
- complete the backup/restore and evidence-verification checks before closing the incident.
Report product vulnerabilities through the Coordinated Vulnerability Disclosure policy.
Appendix A. RC1 Compose service inventory
This release-scoped inventory was produced from the published artifact with:
docker compose --env-file .env.example -f docker-compose.yml \
--profile '*' config --services
It returned 56 services.
STALE as of 2026-09-14 — this inventory describes an older bundle. The service set below has not been re-derived since it was taken, and the published bundle (
devops/release/bundle/docker-compose.yml) has since consolidated well past the three hosts this note’s own sprint touched. Measured at HEAD by parsing that file’sservices:block: 32 top-level keys (infrastructure and one-shot init helpers included), withofflinekit-webandofflinekit-workerpresent andairgap-controller,export-webandexport-workerabsent. Other names below —concelier,doctor-web,packsregistry-*,riskengine-*,issuer-directory,notifier-worker— are likewise gone from the bundle through folds this sprint does not own. The list is left unedited on purpose: a static parse is not thedocker compose --profile '*' config --servicesrun that produced it, and replacing a measured inventory with an inferred one would weaken the claim rather than fix it. Re-deriving it is recorded againstSPRINT_20260722_025for the bundle’s owner.
Default product services (45): advisory-ai-web, advisory-ai-worker, airgap-controller, attestor, attestor-tileproxy, authority, binaryindex-web, concelier, doctor-web, evidence-locker-web, evidence-locker-worker, excititor-web, excititor-worker, export-web, export-worker, findings-ledger-web, graph-api, integrations-web, issuer-directory, notifier-worker, notify-web, opsmemory-web, packsregistry-web, packsregistry-worker, platform, policy-engine, reachgraph-web, registry-token, release-orchestrator, replay-web, riskengine-web, riskengine-worker, router-gateway, sbomservice, scanner-web, scanner-worker, scheduler-web, signals, signer, symbols, timeline-web, tsa, unknowns-web, vexhub-web, vexlens-web.
One-shot initialization helpers (4): advisory-ai-data-init, doctor-evidence-init, release-orchestrator-agent-ca-init, scanner-cache-init.
Infrastructure services (4): postgres, valkey, rustfs, registry.
Opt-in profiles (2): agent-core (agents) and airgap-time (airgap).
Any change to this list, the manifest image list, a published port, network, secret input, or generated key path requires this guide to be re-reconciled.
Change log and review cadence
| Guide revision | Date | Release scope | Change |
|---|---|---|---|
| 3.0 | 2026-08-04 | v1.0.0-RC1 | Rebuilt from the retained signed public artifact: exact service/image inventory, Compose networks and ports, RC1 registry exposure, secret ownership/rotation, and removal of unsupported Kubernetes and single-backend guidance |
| 2.0 | 2025-07-12 | Historical draft | Earlier generic host/runtime guidance; superseded because it did not describe the shipped RC1 architecture |
The release owner must re-run this reconciliation at every RC and GA cut. The guide review is a release gate: compare the signed manifest, resolved Compose services/images/networks/ports, .env.example secret inputs, installer-generated key paths, and applicable rotation runbooks. Do not carry this RC1 service list forward into another release without repeating those checks.
