Stella Ops Trust Bootstrap Guide

Sprint: SPRINT_20260125_003 - WORKFLOW-001 Last updated: 2026-01-25 Status (2026-05-30 reconciliation): DRAFT / partially implemented. The bootstrap shell scripts (devops/scripts/bootstrap-trust.sh, bootstrap-trust-offline.sh) exist and their flags match this guide, but the stella trust * CLI subcommands they orchestrate are not wired into the shipped CLI. The TrustCommandGroup class (src/Cli/StellaOps.Cli/Commands/Trust/TrustCommandGroup.cs) is fully written but its BuildTrustCommand factory is never registered in the root command (src/Cli/StellaOps.Cli/Commands/CommandFactory.cs), so stella trust init, stella trust status, stella trust import, etc. are not invocable today. stella trust snapshot export is additionally a no-op stub that returns NotImplemented. Sections below are annotated inline where the documented command does not match the code. Do not treat the stella trust/stella sign/ stella keys generate examples as working commands until the command group is registered.

Overview

Audience: operators standing up a new Stella Ops deployment who need to establish trust before signing or verifying any attestation.

This guide covers the initial trust setup for a new Stella Ops deployment. Trust bootstrap establishes the cryptographic foundations for secure attestation and verification — pinning a TUF root, importing trusted Rekor keys, and (optionally) provisioning a local signing key.

Prerequisites

Quick Start

The wrapper scripts below ship in devops/scripts/ and accept exactly the flags shown (verified against the script source). However, internally they call stella trust init / stella trust import (with no fallback), and that subcommand group is not currently registered in the CLI — so the scripts will fail at the TUF-init / import step until BuildTrustCommand is wired into CommandFactory. See the status banner at the top of this guide.

Online Bootstrap

# Initialize trust from organization's TUF repository
./devops/scripts/bootstrap-trust.sh \
  --tuf-url https://trust.example.com/tuf/ \
  --pin rekor-key-v1

Offline Bootstrap (Air-Gapped)

# Import pre-packaged trust bundle
./devops/scripts/bootstrap-trust-offline.sh \
  /media/usb/trust-bundle-2026-01-25.tar.zst

Detailed Steps

Step 1: Generate Signing Keys (Optional)

If using local signing keys (not keyless/OIDC):

# Create key directory
mkdir -p /etc/stellaops/keys
chmod 700 /etc/stellaops/keys

# Generate an ECDSA P-256 signing key with OpenSSL.
# (This is what the bootstrap scripts actually use to produce a usable key.)
openssl ecparam -name prime256v1 -genkey -noout \
  -out /etc/stellaops/keys/signing-key.pem
chmod 600 /etc/stellaops/keys/signing-key.pem

NOTE — CLI key generation: the documented stella keys generate --type ecdsa-p256 --out ... form does not exist. There is no top-level stella keys command at all — the only registered top-level key commands are stella key (singular; attestation signing-key rotation, from Proof.KeyRotationCommandGroup) and stella issuer keys (list/create/rotate/revoke, which today emit hard-coded mock JSON, not real key material — CommandFactory.BuildIssuerCommand). The crypto-provider key generator is stella crypto keys generate --algorithm {rsa|ecdsa|ed25519} [--size N] --output <prefix> (intended to write <prefix>.key / <prefix>.pub), but in the current build that handler is a stub — it prints the intended paths without producing real key material (src/Cli/StellaOps.Cli/Commands/CryptoCommandGroup.cs, BuildKeysCommand). Use OpenSSL (above) for a real key until the stub is implemented. bootstrap-trust.sh invokes stella keys generate --type ecdsa-p256 --out ... first; because no such command exists (wrong group and wrong flag names — the real stub uses --algorithm/--output, not --type/--out), that call errors and is silently swallowed by the || openssl … fallback, which is why the script still yields a working key.

Step 2: Initialize TUF Client

NOT WIRED: stella trust init and stella trust status are implemented in TrustCommandGroup but the group is not registered in the CLI root, so these invocations currently fail with an unknown-command error. The option set shown (--tuf-url, --service-map, --pin, plus --cache-path, --offline, --force, --output) does match the code once the group is wired. NOTE: each --pin flag takes a single value; to pin multiple keys, repeat the flag (--pin rekor-key-v1 --pin rekor-key-v2) rather than listing them space-separated after one flag.

# Initialize with your organization's TUF repository
stella trust init \
  --tuf-url https://trust.example.com/tuf/ \
  --service-map sigstore-services-v1 \
  --pin rekor-key-v1 --pin rekor-key-v2

# Verify initialization
stella trust status

The --pin option specifies which Rekor keys to trust. Pin multiple keys during rotation periods.

Step 3: Verify TUF Metadata

NOT WIRED: same caveat as Step 2 — stella trust status is not currently registered. The --show-keys / --show-endpoints flags match the code.

# Check trust status
stella trust status --show-keys --show-endpoints

# Expected output:
# TUF Repository: https://trust.example.com/tuf/
# Service Map: sigstore-services-v1
# Trusted Keys:
#   - rekor-key-v1 (expires: 2027-01-01)
#   - rekor-key-v2 (expires: 2028-01-01)
# Endpoints:
#   - Rekor: https://rekor.sigstore.dev
#   - Fulcio: https://fulcio.sigstore.dev

Step 4: Test Sign/Verify Cycle

INCORRECT (fixed below): there is no top-level stella sign command, and the top-level stella verify command is a unified verifier for attestations/VEX/SBOMs/evidence bundles — not a <file> --sig signature checker. The crypto-provider sign/verify commands are stella crypto sign --input <file> --output <sig> and stella crypto verify --input <file> --signature <sig> (src/Cli/StellaOps.Cli/Commands/CryptoCommandGroup.cs). bootstrap-trust.sh itself uses an OpenSSL fallback for this step, shown below.

# Create a test payload
echo "StellaOps bootstrap test" > /tmp/test-payload.txt

# Sign with your key via the crypto provider (DSSE format by default)
stella crypto sign \
  --input /tmp/test-payload.txt \
  --output /tmp/test.sig

# Verify the signature
stella crypto verify \
  --input /tmp/test-payload.txt \
  --signature /tmp/test.sig

# OpenSSL fallback (what bootstrap-trust.sh actually runs):
#   openssl dgst -sha256 -sign /etc/stellaops/keys/signing-key.pem \
#     -out /tmp/test.sig /tmp/test-payload.txt

# Clean up
rm /tmp/test-payload.txt /tmp/test.sig

Step 5: Test Attestation Sign/Verify (Online Only)

INCORRECT (fixed below): there is no stella attest create subcommand and no --rekor-submit / --check-inclusion flags. The real attestation commands (src/Cli/StellaOps.Cli/Commands/CommandFactory.cs, BuildAttestCommand) are:

  • stella attest sign — creates and signs a DSSE envelope; takes --predicate <file>, --predicate-type <uri>, --subject <name>, --digest <alg:hex>, optional --key or --keyless.
  • stella attest verify — verifies a DSSE envelope offline; takes --envelope <path> (required) plus --policy, --root, --transparency-checkpoint, --require-timestamp. There is no positional file argument and no --check-inclusion; transparency-log inclusion is checked by supplying a --transparency-checkpoint, and stella attest show --id <id> --include-proof surfaces the Rekor inclusion proof.
# Create and sign a DSSE attestation for a subject
stella attest sign \
  --predicate /tmp/predicate.json \
  --predicate-type https://slsa.dev/provenance/v1 \
  --subject test-payload \
  --digest sha256:<hex>

# Verify the envelope (offline) against trust roots
stella attest verify \
  --envelope /tmp/test.intoto.jsonl \
  --transparency-checkpoint /path/to/checkpoint

Offline Bootstrap

For air-gapped deployments without network access:

Create Trust Bundle (Connected System)

On a system with network access, create a trust bundle:

NOT IMPLEMENTED + flag mismatch: stella trust snapshot export is currently a stub — the handler returns NotImplemented and prints “No placeholder trust snapshot was created” (src/Cli/StellaOps.Cli/Commands/Trust/TrustCommandHandlers.cs, HandleSnapshotExportAsync). It also requires real TUF metadata, Rekor tiles, a signed checkpoint, and a manifest writer before it can emit a bundle. There is no --include-tiles flag; the real options are --out (required), --tiles <dir> (include a local tiles directory), --from-proxy, --include-entries <range>, and --depth <n> (default 10000). The example below reflects the actual flag names but will not produce a bundle until the handler is implemented.

Note the related-but-distinct verb stella trust export --out <dir> [--include-targets] (TrustCommandHandlers.HandleExportAsync) is implemented — it copies the cached TUF metadata (root.json, snapshot.json, timestamp.json, targets.json, trust-config.json, plus the targets/ directory) into an output directory, not a tar.zst archive. It still lives in the same unregistered trust group, so it is not invocable today either, and it does not bundle Rekor tiles/checkpoints the way snapshot export is intended to.

stella trust snapshot export \
  --out trust-bundle-$(date +%Y-%m-%d).tar.zst \
  --depth 10000

Transfer and Import (Air-Gapped System)

# Transfer bundle via USB, DVD, or approved data diode
# Then import:
./devops/scripts/bootstrap-trust-offline.sh \
  /media/usb/trust-bundle-2026-01-25.tar.zst

# Optional: Reject stale bundles
./devops/scripts/bootstrap-trust-offline.sh \
  /media/usb/trust-bundle-2026-01-25.tar.zst \
  --reject-if-stale 7d

Configuration Options

TUF Client Configuration

After bootstrap, TUF client configuration is stored under the local application data directory in StellaOps/TufCache/ (TrustCommandHandlers.GetDefaultCachePathEnvironment.SpecialFolder.LocalApplicationData). On Linux this resolves to ~/.local/share/StellaOps/TufCache/; on Windows it is %LOCALAPPDATA%\StellaOps\TufCache\. Override it per-invocation with stella trust init --cache-path <dir> (the --cache-path option) — there is no environment variable for this (see the corrected list below).

Key files:

Environment Variables

ORPHANED / NOT IMPLEMENTED: none of STELLAOPS_TUF_CACHE, STELLAOPS_LOG_LEVEL, or STELLAOPS_OFFLINE is read by the CLI — they have no effect (verified: no references in src/Cli/StellaOps.Cli). Use the equivalent command flags instead:

  • Cache directory — pass --cache-path <dir> to stella trust init (there is no env-var override).
  • Debug logging — pass the global --verbose flag (or -v) to any command.
  • Offline mode — pass --offline to stella trust init / stella trust verify. (For offline content mirrors/kits, the CLI does read STELLAOPS_OFFLINE_KITS_DIRECTORY and STELLAOPS_OFFLINE_MIRROR_URL (src/Cli/StellaOps.Cli/Configuration/CliBootstrapper.cs), but those are unrelated to TUF trust state.)
# Override cache directory (per-invocation flag, not an env var)
stella trust init --tuf-url ... --cache-path /custom/path

# Enable debug logging
stella trust status --verbose

# Offline mode (no network calls)
stella trust init --tuf-url ... --offline

Troubleshooting

Error: “TUF metadata verification failed”

The TUF root key may have been rotated. Obtain the new root.json from your security team and re-bootstrap:

FLAG MISMATCH: stella trust init has no --root-json option (its only flags are --tuf-url, --service-map, --pin, --cache-path, --offline, --force, --output; see TrustCommandGroup.BuildInitCommand). To re-bootstrap against a rotated root, re-run with --force (which re-initializes even if already initialized) pointed at the repository serving the new root metadata.

stella trust init \
  --tuf-url https://trust.example.com/tuf/ \
  --force

Error: “Rekor connectivity check failed”

  1. Verify network access to Rekor endpoint
  2. Check firewall rules for HTTPS (port 443)
  3. Verify the Rekor URL in service map is correct
  4. Try forcing a sync: stella trust sync --force

Error: “Key not found in trust store”

The pinned key may not exist in the TUF repository. Check available keys:

stella trust status --show-keys

Offline: “Bundle is stale”

The trust bundle exceeds the staleness threshold. Obtain a fresh bundle from a connected system:

NOT IMPLEMENTED: as noted under “Create Trust Bundle” above, stella trust snapshot export is a stub and will not produce a bundle in the current build. The --reject-if-stale import-side check (and the bootstrap-trust-offline.sh --reject-if-stale wrapper) is implemented, so the staleness error is real even though regenerating the bundle from the CLI is not yet possible.

# On connected system (NOT IMPLEMENTED — see note above)
stella trust snapshot export --out fresh-bundle.tar.zst

# Transfer and import
./devops/scripts/bootstrap-trust-offline.sh fresh-bundle.tar.zst

Maintenance

Periodic Sync

Set up a cron job to keep TUF metadata fresh:

FLAG MISMATCH + NOT WIRED: stella trust sync has no --quiet flag (only --force and --output {text|json}; use --output json if you want machine-parseable, low-noise output). And as noted in the status banner, the stella trust group is not currently registered in the CLI, so this cron job will not run until BuildTrustCommand is wired in.

# Every 6 hours
0 */6 * * * /usr/local/bin/stella trust sync --output json

Updating Air-Gap Bundles

For air-gapped systems, schedule regular bundle updates based on your organization’s freshness requirements (typically 7-30 days).

Next Steps

Distinct from trust bootstrap: the master-key ceremony. This guide covers trust bootstrap (pinning a TUF root + Rekor keys). It is not the same as provisioning the installation’s master key (KEK) that seals secret values at rest. Every install must also complete the setup Master Key step (generate/import + seal/open probe) before storing connector credentials or deployment bundles — see the Secrets & Keys guide.