Skip to content
SENS Platform Docs

Runtime Configuration

Runtime configuration is an explicit deployment contract. Every new variable must have one authoritative application definition and one deliberate mapping for each deployed environment that needs it.

PurposeAuthoritative locationMay contain secrets
Local developer valueslocal .env, copied from .env.examplelocal placeholders only
Variable name, validation, default, and operational impactservice or package configuration schemano values
Portable Helm defaults and mapping shapeinfra/helm/sens-platform/values.yamlno
Test overridessens-platform-infra/environments/test/sens-platform.values.yamlno
Production overridessens-platform-infra/environments/production/sens-platform.values.yamlno
Test application secret valuesOpenBao KV-v2 mount sens-testyes
Production application secret valuesOpenBao KV-v2 mount sens-productionyes
Runtime secret copiesESO-managed Kubernetes Secrets in each namespaceyes
Immutable image tagsgenerated release.values.yaml in each infra environmentno

The local .env file is ignored by Git and is never copied into a container or Kubernetes deployment. GitHub Actions Secrets are for CI/CD credentials, not for application runtime configuration. Argo CD stores references and desired state, not application secret values. The release GitHub App installation token dispatches the test GitOps workflow and grants release-status steps bounded read access to private Production release metadata. Its variable SENS_RELEASE_APP_CLIENT_ID and secret SENS_RELEASE_APP_PRIVATE_KEY are CI/CD configuration, not application runtime values. The client ID comes from the GitHub App’s general settings and is not the numeric App ID. Source-map builds likewise use SENTRY_AUTH_TOKEN as a GitHub Actions Secret, while SENTRY_ORG and the repository-specific project name remain GitHub Actions Variables.

Each deployable service supports explicit single-key references and folder-synchronized Secret references:

services:
platformApi:
env:
SENS_EXAMPLE_MODE: standard
secretEnv:
SENS_EXAMPLE_TOKEN:
secretName: sens-test-platform-api
key: example-token
secretEnvFrom:
- secretName: sens-test-platform-api-runtime
optional: false
reloadOnSecretChange: true
  • env contains non-secret values that may be committed.
  • secretEnv contains only a Kubernetes Secret name and key, never the value.
  • secretEnvFrom imports every key from a named Kubernetes Secret. It is the standard mapping for an OpenBao catalog group synchronized by ESO.
  • reloadOnSecretChange adds an explicit Reloader annotation for all referenced Secrets so a value change rolls only the affected Deployment.
  • The same variable name must not appear in both maps. Helm rendering fails if it does.
  • Missing non-optional Secret references prevent the Pod or Job from starting.

The database migration Job uses the same env and secretEnv contract under databaseMigrations.

SENS_VISUAL_FLOW_BACKUP_CONFIRMED defaults to false. Set it to true only for the controlled upgrade that removes the legacy decoder schema and only after a recoverable backup has been verified. Fresh installations do not need the confirmation because no legacy decoder table exists.

platform-api requires these runtime values:

VariableSecretDefaultBounds / operational impact
SENS_PLATFORM_API_DATABASE_URLyesnonePostgreSQL is authoritative for every protected request and readiness.
SENS_AUTH_JWT_SIGNING_KEYyesnoneBase64url, at least 32 decoded bytes; rotation invalidates access JWTs.
SENS_AUTH_IDENTIFIER_HMAC_KEYyesnoneSeparate base64url key of at least 32 bytes; rotation resets identifier correlation.
SENS_PLATFORM_API_ALLOWED_ORIGINSnoemptyComma-separated exact HTTP(S) origins; empty disables browser refresh/logout.
SENS_PLATFORM_VERSIONno0.0.0-local.0Exact image tag and /version value; its release channel must match SENS_ENVIRONMENT.
SENS_AUTH_ACCESS_TOKEN_TTL_SECONDSno180060–3600; access-JWT lifetime.
SENS_AUTH_REFRESH_IDLE_TTL_SECONDSno6048003600–2592000; sliding inactivity deadline.
SENS_AUTH_MAX_SESSIONS_PER_USERno101–10; a new login revokes the least recently used excess session.
SENS_AUTH_MAX_CONCURRENT_PASSWORD_CHECKSno41–4; bounds Argon2 memory pressure per API replica.

Generate the JWT and identifier-HMAC keys independently. Do not reuse the database password or copy either key between test and production. The test allowlist contains the exact API, web-console, and documentation origins. Production allows the exact web-console and documentation origins.

The web-console has no runtime Secret or OpenBao group. Its validated runtime values are:

VariableDefaultBounds / operational impact
SENS_WEB_CONSOLE_HTTP_HOSTlocal 127.0.0.1Kubernetes sets 0.0.0.0; controls only the listener bind address.
SENS_WEB_CONSOLE_HTTP_PORT8080Integer 1–65535; used by Service and probes.
SENS_WEB_CONSOLE_API_BASE_URLlocal API originPure credential-free HTTP(S) origin; required and HTTPS outside local.
SENS_ENVIRONMENTlocallocal, test, or production; must match the image release channel.
SENS_PLATFORM_VERSION0.0.0-local.0Immutable image tag exposed by /version and browser runtime configuration.

The test API allowlist is exactly https://api.dev.iot-sens.schlossers.at,https://test-frontend.dev.iot-sens.schlossers.at,https://docs.dev.iot-sens.schlossers.at. The Production allowlist is exactly https://app.iot-sens.schlossers.at,https://docs.iot-sens.schlossers.at. Production sets SENS_WEB_CONSOLE_API_BASE_URL to https://api.iot-sens.schlossers.at. These origins grant browser transport only; every request still requires normal authentication and authorization.

OwnerVariableSecretDefault / effect
all Node deployablesSENS_SENTRY_ENABLEDnofalse; keeps Sentry completely disabled
all Node deployablesSENS_SENTRY_DSNconfigurationrequired only when enabled
all Node deployablesSENS_SENTRY_TRACES_SAMPLE_RATEnoProduction 0.05, Test 0.2, local disabled
Platform APISENS_PRODUCT_ANALYTICS_ENABLEDnofalse; controls the additive auth identity
Platform APISENS_PRODUCT_ANALYTICS_ID_HMAC_KEYyesenvironment-specific base64url key, required when analytics is enabled
web consoleSENS_WEB_CONSOLE_SENTRY_ENABLED, SENS_WEB_CONSOLE_SENTRY_DSN, SENS_WEB_CONSOLE_SENTRY_TRACES_SAMPLE_RATEDSN is external configurationoptional browser error reporting
web consoleSENS_WEB_CONSOLE_POSTHOG_ENABLED, SENS_WEB_CONSOLE_POSTHOG_HOST, SENS_WEB_CONSOLE_POSTHOG_PROJECT_KEYproject key is external configurationTest and Production; SDK still requires browser consent

Keep the analytics HMAC key for at least the 12-month event retention. Rotate it only for a security incident because rotation deliberately begins a new pseudonymous identity generation. Source-map CI uses SENTRY_AUTH_TOKEN, SENTRY_ORG, and SENTRY_PROJECT_WEB_CONSOLE; the token is a GitHub Secret, the other two are GitHub Variables, and none enters runtime images.

The authoritative runtime locations are the environment-specific Helm/GitOps values plus the consuming service’s OpenBao catalog group. In particular, SENS_PRODUCT_ANALYTICS_ID_HMAC_KEY belongs to runtime/platform-api in the environment-specific mount and is required whenever analytics is enabled. SENS_SENTRY_DSN belongs to each enabled Node deployable’s runtime configuration. The browser variables SENS_WEB_CONSOLE_SENTRY_DSN, SENS_WEB_CONSOLE_POSTHOG_HOST, and SENS_WEB_CONSOLE_POSTHOG_PROJECT_KEY belong to the web-console runtime configuration. SENS_SENTRY_ENABLED, SENS_SENTRY_TRACES_SAMPLE_RATE, SENS_PRODUCT_ANALYTICS_ENABLED, SENS_WEB_CONSOLE_SENTRY_ENABLED, SENS_WEB_CONSOLE_SENTRY_TRACES_SAMPLE_RATE, and SENS_WEB_CONSOLE_POSTHOG_ENABLED remain explicit, non-secret Helm values. All SaaS settings are optional in Test and Production and stay disabled for local and customer-owned on-premise environments unless deliberately enabled.

The manual IOTA snapshot import intentionally adds no environment variable, Helm value, Kubernetes Secret, OpenBao entry, or persisted platform configuration. A platform administrator enters the constrained IOTA base URL, username, and password for one request in the web console. The Platform API uses them only in memory and the web console clears the password after every attempt.

Do not add IOTA_USERNAME, IOTA_PASSWORD, or an IOTA base URL to .env or to deployment configuration for this feature. Kubernetes needs only outbound DNS and HTTPS reachability from platform-api to the validated commonapi-<slug>.xaas-a0a0.cloud host. IOTA availability is not part of platform health or readiness. See IOTA snapshot import v1 for the temporary contract and mandatory removal checklist.

mqtt-ingestion-worker validates the following receiver values at startup:

VariableSecretContract
SENS_MQTT_INGESTION_HTTP_HOSTnolocal 127.0.0.1, Kubernetes 0.0.0.0
SENS_MQTT_INGESTION_HTTP_PORTnodefault 3001
SENS_NETMORE_MQTT_URLnomqtts:// only; default Netmore port 8883
SENS_NETMORE_MQTT_TOPICnoexact sensor/sensor_network_services/+/payload
SENS_NETMORE_MQTT_USERNAMEyesrequired shared portal user
SENS_NETMORE_MQTT_PASSWORDyesrequired and rotated before live use
SENS_NETMORE_MQTT_CLIENT_IDyesrequired stable, distinct per environment
SENS_NETMORE_EXCLUDED_CUSTOMERSnorequired exact topic-segment list
SENS_NETMORE_MAX_PAYLOAD_BYTESnodefault and maximum 262144
SENS_NATS_URLnorequired cluster-internal URL
SENS_NATS_USERNAMEyespublisher identity
SENS_NATS_PASSWORDyespublisher identity
SENS_NATS_PUBLISH_TIMEOUT_MSnodefault 5000

raw-storage-worker uses SENS_RAW_STORAGE_HTTP_HOST, SENS_RAW_STORAGE_HTTP_PORT, existing database configuration, consumer NATS credentials, and SENS_RAW_CONSUMER_CONCURRENCY with default 4 and bounds 1–16.

The raw-ingestion-retention Job requires SENS_DATABASE_URL, uses SENS_RAW_RETENTION_DAYS=30 for the registered-device cutoff, and uses SENS_RAW_UNREGISTERED_DEVICE_RETENTION_HOURS=168 for rows whose DevEUI is not in the platform registry when cleanup runs. The unregistered cutoff must be shorter than the general raw-retention period. It also uses SENS_OUTBOX_PUBLISHED_RETENTION_DAYS=7 to delete delivered transactional outbox events by published_at. The value is bounded to 1–365 days. Pending events with published_at IS NULL are never eligible for retention.

rawIngestionBootstrap.migrateRawStoredRetention is a non-secret Helm-only maintenance switch and defaults to false. When enabled, the bootstrap command receives --migrate-raw-stored-retention. Helm requires the bootstrap Job and retention CronJob to remain enabled and requires MQTT ingestion, raw storage, and the Flow worker to be disabled. No new environment variable or Secret is introduced.

NATS server authentication, bootstrap, publisher, and consumer credentials are referenced from the environment-specific sens-*-nats-auth Secret. The Netmore folder supplies only username, password, and the environment’s stable client ID. Test and production share the newly rotated username/password by explicit agreement, but never the client ID. Coordinated password rotation restarts both receiver Deployments and can interrupt both feeds at once.

All three Flow workloads remain disabled by default. The Platform API may only enable sandbox requests when the sandbox worker is enabled and reachable.

OwnerVariableSecretDefault and effect
Platform APISENS_FLOW_SANDBOX_REQUESTS_ENABLEDnofalse; permits custom-script release loads and preview execution
Platform APISENS_FLOW_TEST_RUN_TIMEOUT_MSno5000; total synchronous preview deadline
Platform APISENS_SANDBOX_REQUEST_TIMEOUT_MSno500; NATS request/reply deadline
Platform APISENS_NATS_URL, SENS_NATS_USERNAME, SENS_NATS_PASSWORDcredentials yessandbox requester identity; required only when requests are enabled
Flow workerSENS_FLOW_WORKER_HTTP_HOST, SENS_FLOW_WORKER_HTTP_PORTnooperations listener, default port 3003
Flow workerSENS_RAW_STORED_CONSUMER_CONCURRENCYnobounded Raw Stored concurrency, default 4
Flow workerSENS_FLOW_SANDBOX_REQUESTS_ENABLEDnofalse; custom-script nodes fail closed when disabled
Flow workerSENS_SANDBOX_REQUEST_TIMEOUT_MSno500; bounded sandbox request
Sandbox workerSENS_SANDBOX_HTTP_HOST, SENS_SANDBOX_HTTP_PORTnooperations listener, default port 3004
Sandbox workerSENS_SANDBOX_EXECUTION_TIMEOUT_MSno100; QuickJS interrupt deadline
Sandbox workerSENS_SANDBOX_MEMORY_LIMIT_BYTESno16777216 (16 MiB) per request
Sandbox workerSENS_SANDBOX_MAX_INPUT_BYTES, SENS_SANDBOX_MAX_OUTPUT_BYTESno262144 each
Action workerSENS_FLOW_ACTION_HTTP_HOST, SENS_FLOW_ACTION_HTTP_PORTnooperations listener, default port 3005
Action workerSENS_FLOW_ACTION_CONCURRENCYnobounded consumer concurrency, default 4
Action workerSENS_FLOW_ACTIONS_ENABLEDnofalse; when false no Action consumer starts
Action workerSENS_FLOW_ACTION_MAX_ATTEMPTSno10; durable job attempts before Dead Letter
Action workerSENS_FLOW_EMAIL_RATE_LIMIT_PER_TENANT_PER_MINUTEno60; PostgreSQL-shared minute bucket
Action workerSENS_SMTP_HOST, SENS_SMTP_PORT, SENS_SMTP_SECURE, SENS_SMTP_FROMnodeployment-wide SMTP endpoint; host/from are required when Actions are enabled
Action workerSENS_SMTP_AUTH_REQUIRED, SENS_SMTP_REQUIRE_TLSnoexplicit authentication and STARTTLS gates; both are true in test/production
Action workerSENS_SMTP_USERNAME, SENS_SMTP_PASSWORDyescomplete credential pair required for authenticated Actions

Every worker also requires its own SENS_NATS_USERNAME and SENS_NATS_PASSWORD. Identities are not interchangeable: Flow Worker, Action Worker, Platform API and Sandbox have distinct publish/subscribe permissions. Database URLs are required only by Platform API, Flow Worker and Action Worker; the sandbox must never receive one.

The chart-level monitoring.prometheusRule.enabled switch optionally renders the initial Flow Replay backlog, Dead Letter recorder backlog, and sandbox failure rules. Its thresholds and windows are configured below monitoring.prometheusRule. The switch defaults to false; enabling it requires the target cluster to provide the Prometheus Operator PrometheusRule CRD.

embedded.nats.metricsExporter.enabled controls the metrics-only NATS sidecar and defaults to true. embedded.nats.metricsExporter.image.repository, .tag, .pullPolicy, and .resources are the portable image and resource contract. The exporter reads NATS’ Pod-local monitoring endpoint and exposes only Prometheus metrics through the existing platform Service selector; it has no credentials and adds no Secret or environment variable.

  1. Add the variable to the owning service or package configuration schema with validation, default behavior, and operational-impact metadata.
  2. Add a safe local example to .env.example when developers need the value.
  3. Add the portable default or mapping to the correct env map in infra/helm/sens-platform/values.yaml.
  4. Add test and production overrides only where their values differ, using the static sens-platform.values.yaml files in the infra repository.
  5. Update the owning module or operations documentation.
  6. Add configuration tests and render both Helm environments.

Do not add runtime variables to release.values.yaml. That file is generated and contains image tags only.

  1. Mark the variable as secret in the owning configuration metadata and avoid values in examples, logs, errors, fixtures, Git, and image layers.
  2. Open the Ops portal and select the correct environment and catalog group.
  3. Add the exact environment-variable key and its value in the UI.
  4. Update the application schema, tests, and documentation when the key is new.
  5. Verify the ExternalSecret Ready condition and the affected rollout without printing or decoding the synchronized Kubernetes Secret.

Workloads import their generated Kubernetes Secret through secretEnvFrom. Most ExternalSecret resources synchronize a complete OpenBao group, so a new key does not need an additional per-key YAML mapping. Raw Storage, Flow Worker, and Flow Action Worker use explicit absolute mappings for the repeated SENS_DATABASE_URL key to avoid recursive provider deduplication; new keys in those folders require a GitOps mapping update. A new deployable service needs a dedicated folder and GitOps mapping. See Secret Management for the complete UI, bootstrap, isolation, failure, and recovery model.

The infra repository separates configuration from release selection:

  • sens-platform.values.yaml is static, reviewed environment configuration.
  • release.values.yaml is generated and contains only immutable image tags.
  • release.yaml records the product commit and image tags for auditability.

The automatic test workflow updates only the test Application revision, release.values.yaml, and release.yaml. It cannot erase newly added runtime configuration. Production promotion updates the equivalent production release files through a reviewed pull request and leaves production configuration unchanged.

The Platform version channel is part of runtime validation: local uses MAJOR.MINOR.PATCH-local.0, test uses MAJOR.MINOR.PATCH-alpha.RUN.ATTEMPT, future staging uses MAJOR.MINOR.PATCH-rc.NUMBER, and Production uses stable MAJOR.MINOR.PATCH. Helm derives SENS_PLATFORM_VERSION from the same platform-api image tag, and runtime checks require exact equality. See Platform Release Version for the three release-state sources, bump workflow, and failure behavior.

Production release authorization belongs to SENS Platform Ops RBAC plus fresh TOTP. The removed SENS_PRODUCTION_RELEASE_PASSWORD_SCRYPT variable must not be present in local files, Helm, OpenBao, Kubernetes, GitHub Actions, images, or runtime environments.

Every release publishes a dedicated database-migrations image. Argo CD sync waves apply TimescaleDB and NATS first, the database migration second, the raw stream bootstrap next, then the raw consumer, and finally the MQTT receiver. The migration runner waits for database readiness and applies repeatable, versioned SQL migrations. A failed migration blocks the later rollout wave.

Test and production reuse the existing TimescaleDB authentication Secret. The migration Job receives the password through secretEnv; host, port, database, and user are non-secret environment values. TimescaleDB policy migrations stay disabled while the embedded OSS image does not support them.

Run before merging a runtime-configuration change:

Terminal window
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm helm:lint
pnpm helm:template:test
pnpm helm:template:production
pnpm helm:check-runtime-config

Review the rendered resources for the intended namespace, Secret names, non-secret values, migration image tag, and Argo CD sync waves.

The flow worker uses SENS_FLOW_WORKER_HTTP_HOST, SENS_FLOW_WORKER_HTTP_PORT, SENS_RAW_STORED_CONSUMER_CONCURRENCY, and SENS_SANDBOX_REQUEST_TIMEOUT_MS. It also requires the documented database and NATS credentials.

The isolated sandbox uses SENS_SANDBOX_HTTP_HOST, SENS_SANDBOX_HTTP_PORT, SENS_SANDBOX_EXECUTION_TIMEOUT_MS, SENS_SANDBOX_MEMORY_LIMIT_BYTES, SENS_SANDBOX_MAX_INPUT_BYTES, and SENS_SANDBOX_MAX_OUTPUT_BYTES. Its Pod receives no database or SMTP secrets.

The action worker uses SENS_FLOW_ACTION_HTTP_HOST, SENS_FLOW_ACTION_HTTP_PORT, SENS_FLOW_ACTION_CONCURRENCY, SENS_FLOW_ACTIONS_ENABLED, and SENS_FLOW_EMAIL_RATE_LIMIT_PER_TENANT_PER_MINUTE. SMTP is configured with SENS_SMTP_HOST, SENS_SMTP_PORT, SENS_SMTP_SECURE, and SENS_SMTP_FROM; test and production additionally set SENS_SMTP_AUTH_REQUIRED=true and SENS_SMTP_REQUIRE_TLS=true. SENS_SMTP_USERNAME and SENS_SMTP_PASSWORD are Kubernetes Secret references with no defaults. When Actions are enabled but host, sender, username, or password is missing, startup fails. Delivery also fails when STARTTLS cannot be negotiated. Test and production start with Actions disabled.

The reviewed non-secret sender for test and production is SENS_SMTP_FROM=sens.ops@sens.at. Changing it requires synchronized product Helm values, generated infra release values, runtime-contract checks, and SMTP sender authorization. It does not replace the separate SMTP credentials.