Files
sencho/.env.example
T
Anso 341511a2e0 feat: deliver hub registry credentials to remote Compose targets (#1866)
* feat: deliver hub registry credentials to remote Compose targets

When a hub forwards stack operations to a remote node over confidential
transport, discover private image hosts on the target, attach an attested
credential envelope, and materialize DOCKER_CONFIG at the Compose seam.
Capability-gated with pass-through when delivery is unavailable.

* fix: satisfy CI for registry delivery seam and git apply locks

Defer delivery_source_id lookup until registry auth is materialized, reset
stack op locks between git-source tests, and mock docker auth temp dirs in
compose-service registry auth tests.

* fix: clear ESLint errors in registry delivery files

Remove unused imports and dead helpers, use const where appropriate, and
reorder compose abort handler setup to satisfy prefer-const.

* fix: harden registry discovery paths and stabilize git-transport timing

Validate stack names and resolve project paths against compose roots before
filesystem discovery. Widen the git-transport termination race margin in CI.

* fix: carry resolvedRefKind through git candidate prepared metadata

After merging main, FetchResult requires resolvedRefKind. Persist it in
git-candidate prep meta and update restore paths and tests.

* fix: satisfy CodeQL path, race, and log-injection findings

Add inline path barriers at registry delivery filesystem sinks, drop
stat-then-read TOCTOU patterns, sanitize discover error logs, and bound
body-content compose writes.

* fix: clear remaining ESLint and CodeQL findings on PR 1866

Remove unsafe throw from finally, tighten path barriers and candidate
validation, eliminate stat-then-read races, and scope CodeQL http-to-file
exclusion for discover staging.

* fix: resolve remaining CodeQL alerts for registry delivery PR

Route template env writes through FileSystemService, use mkdtemp for
discover staging, share payload copy helper with materialize, and add
targeted CodeQL query exclusions for validated delivery paths.

* fix: discover body-content registry refs in memory

Avoid staging hop-1 compose YAML to disk by hashing and scanning inline
content, eliminating the remaining http-to-file CodeQL finding.

* fix: clear CodeQL alerts surfaced by GitSourceService diff

Harden runDockerCompose cwd, sanitize diag log output, validate template
service names, and simplify compose path interpolation detection.

* fix: extract docker compose runner for CodeQL path barrier

Move spawn-based compose validation into a dedicated helper with a
documented path-injection exclusion, clearing the last PR CodeQL alert.

* fix: restore GitSourceService runDockerCompose wrapper for tests

Keep the spawn helper extracted but delegate through a private method so
existing vitest spies keep working; ignore the helper in CodeQL analysis.

* fix: remediate registry delivery audit findings (C-01 through S-08)

Load stack .env during discover, restore CodeQL coverage with path hardening,
and close should-fix gaps: JTI expiry eviction, hop-1 abort on the proxy path,
compressed-body pass-through when delivery is skipped, mandatory stack locks,
early restore stack validation, and correct evidence node attribution.

* fix: satisfy CodeQL path and property injection on compose helpers

Hoist docker compose spawn out of the Promise executor so the cwd barrier
is in the same scope as the sink, and ignore unsafe request env keys.

* fix: correct compose-env test expectation and reshape path-injection guard

The new unsafe-key test asserted an exact object shape that ignored the
documented process.env override layer, failing wherever process.env is
non-empty. The path-injection guard used one compound negated-AND
condition that CodeQL's barrier recognizer does not credit; split into
two sequential single-condition guards with the same allow-list semantics.

* fix: align blueprint registry discover with seam and harden proxy abort

Stage blueprint post-apply bundles for body-content discovery so hop-1
hash and hosts match the seam when an existing stack .env is present.
Restore prior compose.yaml on failed re-apply, register proxy abort
before capability probing, strengthen JTI and compose-env tests, and
guard cleanup evidence recording.

* fix(registry-delivery): remove unused stackName local in discoverOnTarget

ESLint flagged a leftover local from the audit-findings remediation pass; the stack name is already resolved separately where it is actually used.

* fix: stop proxy on registry delivery abort and fail-closed blueprint snapshot

Return a distinct aborted decision from the registry delivery proxy gate so
client disconnect during capability probing does not forward consequential
requests. Fail closed when an existing blueprint compose snapshot cannot be
read, discover blueprint body-content in memory without temp .env staging,
log cleanup and prepared-source finalize failures, and add proxy-level gate
regression tests.

* fix(registry-delivery): remove unused fs local in blueprint snapshot-fail test

* fix: complete registry delivery abort coverage and empty .env hash parity

Check abort after hub envelope construction and before proxy forward so
client disconnect during credential resolution cannot reach hop 2. Include
zero-byte stack .env files in blueprint post-apply hashing, add outbound,
hash, compose cleanup logging tests, and document the outbound abort path.

* fix: classify registry delivery routes under /api mount prefix

Express strips the mount prefix from req.path when registryDeliveryMiddleware
is installed at app.use('/api', ...). Normalize to /api${req.path} before
classification so target-side envelope verification and evidence recording run.

Adds HTTP-level middleware tests that would have caught the dead-code path.
2026-08-29 19:07:36 -04:00

176 lines
7.5 KiB
Bash

# Sencho Configuration
# Copy this file to .env and update the values for production
# Directory containing docker-compose files
COMPOSE_DIR=/path/to/your/compose/files
# Database and state directory inside the container (default: /app/data)
DATA_DIR=/app/data
# Optional path to a host-installed Trivy binary. Sencho first looks for a
# managed install under DATA_DIR/bin/trivy; if absent and TRIVY_BIN is set,
# Sencho uses that path; otherwise it falls back to `trivy` on PATH. Once a
# managed install is present, the managed copy takes precedence over this.
TRIVY_BIN=
# Node environment (set automatically in Docker image; only change for local dev)
NODE_ENV=production
# Frontend URL for CORS in production (leave empty for same-origin setups)
FRONTEND_URL=
# Base URL for the Hardened Build assurance API
SENCHO_ASSURANCE_API=https://sencho.io
# Non-production/test-only entitlement stub (1, entitled, unauthorized, unavailable).
# Ignored when NODE_ENV=production.
SENCHO_ASSURANCE_ENTITLEMENT_STUB=
# Global API rate limit (requests per minute per user session, production only)
# Authenticated requests are keyed by user ID; unauthenticated by IP.
# Internal node-to-node traffic (node_proxy tokens) bypasses this limit entirely.
API_RATE_LIMIT=200
# Polling endpoint rate limit (requests per minute, production only)
# Applies to dashboard/status polling endpoints that are exempt from the global limit.
# Increase for environments with many concurrent browser sessions behind shared NAT.
API_POLLING_RATE_LIMIT=300
# Public URL of THIS primary instance, used to bake an externally-reachable
# SENCHO_PRIMARY_URL into pilot enrollment YAML. Set this when the primary
# sits behind a reverse proxy or Cloudflare Tunnel so pilots on a different
# network can dial the public hostname instead of the request Host header
# the admin happens to be on (loopback, LAN, etc.). Must be http(s)://; no
# trailing slash. When unset, enrollment falls back to the request Host.
SENCHO_PUBLIC_URL=
# ─── Pilot agent (remote host only) ──────────────────────────────
# These three vars are required ONLY on a remote host running as a
# pilot-agent reverse-tunnel container. The primary instance does not
# read them. See docs/features/pilot-agent.mdx for the full setup.
# Switches the container into agent mode. The primary instance leaves
# this unset.
SENCHO_MODE=
# Comma-separated CIDRs of reverse proxies trusted to set X-Forwarded-Proto
# for Pilot Agent TLS termination. Unset or invalid: non-TLS Pilot upgrades are
# treated as non-confidential and hub registry credential delivery is skipped.
SENCHO_TRUSTED_PROXY_CIDRS=
# WebSocket-capable URL of the controlling Sencho instance. Use https://
# scheme; the agent rewrites it to wss:// for the tunnel upgrade.
SENCHO_PRIMARY_URL=
# Single-use, 15-minute enrollment token issued by the primary when the
# pilot-agent node is created. After the first successful connect the
# agent persists a long-lived tunnel credential at /app/data/pilot.jwt
# and ignores this var on subsequent restarts.
SENCHO_ENROLL_TOKEN=
# Optional: path inside the agent container to a PEM CA bundle the
# agent should trust when validating the primary's TLS cert. Use this
# for self-hosted deployments terminating TLS with an internal CA.
# Leave unset to fall back to the system trust store. There is no
# escape hatch to disable TLS verification.
SENCHO_PILOT_CA_FILE=
# ─── SSO / LDAP Configuration ────────────────────────────────────
# LDAP / Active Directory
SSO_LDAP_ENABLED=false
SSO_LDAP_URL=ldap://ldap.example.com:389
SSO_LDAP_BIND_DN=cn=readonly,dc=example,dc=com
SSO_LDAP_BIND_PASSWORD=
SSO_LDAP_SEARCH_BASE=ou=users,dc=example,dc=com
SSO_LDAP_SEARCH_FILTER=(uid={{username}})
SSO_LDAP_ADMIN_GROUP_DN=
SSO_LDAP_DEFAULT_ROLE=viewer
SSO_LDAP_DISPLAY_NAME=LDAP
SSO_LDAP_TLS_REJECT_UNAUTHORIZED=true
# Google OIDC
SSO_OIDC_GOOGLE_ENABLED=false
SSO_OIDC_GOOGLE_CLIENT_ID=
SSO_OIDC_GOOGLE_CLIENT_SECRET=
# GitHub OAuth
SSO_OIDC_GITHUB_ENABLED=false
SSO_OIDC_GITHUB_CLIENT_ID=
SSO_OIDC_GITHUB_CLIENT_SECRET=
# Okta OIDC
SSO_OIDC_OKTA_ENABLED=false
SSO_OIDC_OKTA_ISSUER_URL=
SSO_OIDC_OKTA_CLIENT_ID=
SSO_OIDC_OKTA_CLIENT_SECRET=
# Custom OIDC (Keycloak, Authentik, Authelia, Zitadel, etc.)
SSO_OIDC_CUSTOM_ENABLED=false
SSO_OIDC_CUSTOM_DISPLAY_NAME=Custom OIDC
SSO_OIDC_CUSTOM_ISSUER_URL=
SSO_OIDC_CUSTOM_CLIENT_ID=
SSO_OIDC_CUSTOM_CLIENT_SECRET=
SSO_OIDC_CUSTOM_SCOPES=openid email profile
SSO_OIDC_CUSTOM_ID_CLAIM=
SSO_OIDC_CUSTOM_USERNAME_CLAIM=
SSO_OIDC_CUSTOM_EMAIL_CLAIM=
# Role mapping (shared across OIDC providers)
SSO_OIDC_ADMIN_CLAIM=groups
SSO_OIDC_ADMIN_CLAIM_VALUE=sencho-admins
SSO_DEFAULT_ROLE=viewer
# External base URL for OAuth callback URLs (required behind reverse proxy)
SSO_CALLBACK_URL=
# Idle teardown for the persistent mesh proxy tunnel central opens to
# Distributed API (proxy-mode) remotes. The tunnel stays open for the
# life of the WebSocket. Set a positive number of milliseconds to
# enable idle teardown after that many ms of zero active mesh streams;
# the next mesh dial re-opens it. Default 0 (persistent).
SENCHO_MESH_PROXY_TUNNEL_IDLE_MS=0
# Mesh network subnet for this node. Leave unset to let Sencho pick the first
# free /24 from its candidate list (172.30.0.0/24, 172.31.0.0/24, 10.42.0.0/24,
# 10.43.0.0/24), or adopt the existing sencho_mesh network's subnet when it is
# already present. Set a specific CIDR only when you need to avoid an overlap
# with another network on this host. Configured independently per node.
# SENCHO_MESH_SUBNET=10.42.0.0/24
# Maximum bytes a single Git Source clone may download from the Git host before
# it is aborted. This bounds network transfer and abuse, not the decompressed
# on-disk size. A shallow clone of a compose repo is tiny; raise this only if
# you track compose files in a legitimately large repository. Default
# 104857600 (100 MB).
GITSOURCE_MAX_CLONE_BYTES=104857600
# Complete-project materialization bounds (Git Sources)
GITSOURCE_MAX_MATERIALIZED_FILES=10000
GITSOURCE_MAX_MATERIALIZED_BYTES=536870912
GITSOURCE_MAX_BUILD_CONTEXT_BYTES=268435456
GITSOURCE_MAX_PATH_DEPTH=64
GITSOURCE_MAX_FILE_BYTES=10485760
# Idle-output backstop for deploy and update compose steps (pull/recreate). If a
# step produces no output for this long while still running, Sencho treats it as
# stalled and stops it, so a hung image pull surfaces a clear failure (and the
# in-app recovery actions) instead of spinning. Conservative by default because a
# working pull can be briefly silent while a large layer extracts; raise it on
# slow links or heavy local builds. Default 600000 (10 minutes).
# SENCHO_COMPOSE_STALL_TIMEOUT_MS=600000
# Container-side path to the OpenZFS ARC stats file, for ZFS-aware host memory.
# On ZFS hosts (TrueNAS SCALE, Proxmox, ZFS on Ubuntu/Debian) the ARC cache is
# reclaimable but the kernel reports it as used, which can trigger false
# host-memory alerts. When ARC stats are readable, Sencho adds reclaimable ARC
# back into available memory. Sencho checks this path first, then
# /host/proc/spl/kstat/zfs/arcstats, then /proc/spl/kstat/zfs/arcstats. Set this
# only if your ARC stats live at a non-standard path inside the container. If no
# ARC stats are readable, host memory reporting is unchanged.
# SENCHO_ZFS_ARCSTATS_PATH=
# Path inside the container to /proc/meminfo, for VM memory ballooning awareness.
# Sencho checks this path first, then /host/proc/meminfo, then /proc/meminfo.
# Set it only when your meminfo lives at a non-standard path inside the container.
# SENCHO_PROC_MEMINFO_PATH=