Property Reference
This document is regenerated from the actual deployed configuration template (
*.properties) (2026-07-28) — the template's current location isplantpulse-datalake-cli/config/templates, and the host's/etc/kopens/confis bind-mounted to that location. The single source of truth for the deployment path mapping isplantpulse-startup/src/plantpulse/startup/config/ConfigTemplates.java, and the single source of truth for loading priority isplantpulse-core/src/plantpulse/core/engine/utils/PropertiesUtils.java.If you add/remove/rename a key, the template is the source of truth, not this document. Don't hand-fill the document — regenerate it from the template. The "Default" column in each table is exactly as written in the template, and
${PP_XXX}is whereconfigure.shsubstitutes theenv.shvalue.
Three-Stage Configuration Model
| Stage | Location | How to change |
|---|---|---|
| Global config | plantpulse-startup/env.sh | Edit directly, then ./configure.sh → restart the corresponding module |
| Site override | plantpulse-startup/env.local.sh | env.sh is entirely in ${PP_VAR:-default} form, so an already-exported value wins |
| Module config | each module's config/ | Can be edited directly, but will be overwritten the next time configure.sh runs |
Recommendation: In production, edit only
env.sh(orenv.local.sh) and letconfigure.shgenerate all module configurations.
Standard procedure for changing env.sh
cd /opt/kopens/plantpulse-platform/plantpulse-datalake-cli/bin
vi env.sh # 1. 편집
./env-validate.sh # 2. 검증 (선택)
./configure.sh # 3. 템플릿 치환 + 각 모듈 config/ 로 배포
./restart.sh # 4. 재시작 (또는 ./restart-<module>.sh)
./status.sh # 5. 확인
configure.shonly re-renders the configuration files; it does not restart the services. Until a restart, the new configuration exists only on disk while the old process is still alive.
Configuration File Loading Priority
Web applications (server / batch / cep / sql / data-gateway) look for configuration files in the following order. The implementation is PropertiesUtils.readExternalFirst().
- The directory specified via the
-Dpp.conf.dirsystem property (constantPropertiesUtils.CONF_DIR_SYSTEM_PROPERTY = "pp.conf.dir") ${catalina.base}/../config— the module'sconfig/directory- The web app classpath (default bundled in the WAR) — fallback
External files are read as UTF-8. When an external configuration loads successfully, the following is printed in the startup log:
Properties loaded from external conf: <absolute-path>
If reading the external file fails, an error log is written and it falls back to the classpath.
Logging configuration (log4j2.xml) is also externalized through the same chain (self-seeding — if no external file exists, the WAR default is copied once to the module's config/log4j2.xml before use). When applied, Logging reconfigured from external conf: <absolute-path> is printed in the startup log. Log level changes are applied by editing the module's config/log4j2.xml and restarting, and this persists across WAR redeployments. See the configuration and deployment guide for details.
Note: Do not edit internal web app files (e.g.
server/webapps/ROOT/WEB-INF/classes/) directly — they disappear on WAR redeployment.
${ENV:기본값} Substitution Happens Twice
The ${PP_XXX} / ${PP_XXX:기본값} notation in the template is resolved at two points in time.
| Point in time | Actor | Behavior |
|---|---|---|
| At deployment | configure.sh → TemplateProcessor.resolvePlaceholders | Substituted with env.sh. Order is replacements map → environment variables → :기본값 → keep original. Nested placeholders not supported |
| At runtime | PropertiesUtils.resolveEnvPlaceholders | Re-resolves ${ENV_VAR[:default]} against every value in the loaded Properties |
In other words, a ${...} still remaining after deployment can also be filled in from the process's environment variables. This is why a Docker -e VAR=... injection takes precedence over the template default.
PP_* Variables Not in env.sh (the template default applies as-is)
The variables below are referenced by the template as ${PP_XXX:기본값}, but env.sh does not export them. So unless injected separately, the template default is always used.
PP_STORAGE_PASSWORD · PP_METASTORE_PASSWORD · PP_EXTERNAL_DB_PASSWORD · PP_MAIL_SMTP_HOST · PP_MAIL_SMTP_PORT · PP_MAIL_SMTP_AUTH · PP_MAIL_SMTP_USER · PP_MAIL_SMTP_PASSWORD · PP_MAIL_SMTP_STARTTLS · PP_DIAGNOSTIC_EMAIL · PP_AI_OPENAI_CHAT_PATH · PP_AI_OPENAI_RESPONSES_PATH · PP_AI_MCP_ENABLED · PP_AI_MCP_ALLOWED_ORIGINS · PP_AI_CHAT_ENABLED
PP_MAIL_SMTP_* is intentionally not exported. As the env.sh comment notes, there was an incident where an exported environment variable silently overrode the template default (the Brevo relay SSOT), breaking the SMTP login combination (2026-07-22, a stale 25/no-auth value masked the Brevo 587/auth value). Site-specific overrides belong in env.local.sh.
The 5 Server-Managed Configurations
There are exactly 5 configuration files defined by the ConfigTemplates.SERVER_CONFIGS array and deployed via plantpulse-server/config/.
| # | Template | Deployment Path |
|---|---|---|
| 1 | plantpulse-engine.properties | /plantpulse-server/config/plantpulse-engine.properties |
| 2 | plantpulse-storage.properties | /plantpulse-server/config/plantpulse-storage.properties |
| 3 | plantpulse-mq.properties | /plantpulse-server/config/plantpulse-mq.properties |
| 4 | plantpulse-mail.properties | /plantpulse-server/config/plantpulse-mail.properties |
| 5 | plantpulse-ai.properties | /plantpulse-server/config/plantpulse-ai.properties |
Of these, 1–4 are read together by PropertiesLoader.load() (engine/mail/mq/storage) and reconstructed into the typed configuration beans EngineConfig · StorageConfig · MqConfig. Item 5 (plantpulse-ai.properties) is read separately by AIConfig through the same external chain.
plantpulse-startup/sh/server-managed-webapp-files.listis currently empty (comments only). This is because, after the 2026-06 configuration externalization was completed, no server-managed configuration remains inside the WAR.
application.properties (Removed)
application.properties was removed in 2026.06. No code reads this file (zero usages across the entire repo), and its existing entries were migrated as follows.
- Theme / homepage and other console behavior settings → console System > Settings Management (PostgreSQL
mm_configtable) alarm.duplicate.check.minutes→ theplantpulse-engine.propertiesfamily (read byEngineConfig)
plantpulse-engine.properties
Path: plantpulse-server/config/plantpulse-engine.properties · Template: template/plantpulse-engine.properties
Controls the core behavior of the data processing engine.
Async / Pipeline
| Property | Default | Substitution | Description (template comment) |
|---|---|---|---|
engine.async.parallelism | 256 | — | Async worker pool parallelism (total cores ~= 256) |
engine.pipeline.threads | 36 | — | Number of pipeline threads |
engine.pipeline.ratelimit | 40000 | — | Pipeline per-second processing cap (rate limit) |
engine.pipeline.queue.size | 1200000 | — | Pipeline queue size (backpressure limit) |
engine.pipeline.queue.o3.delay | 50 | — | Allowed delay for out-of-order data (ms) |
engine.pipeline.task.mode | SINGLE | — | Task execution mode (SINGLE / PARALLEL) |
NTP
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.ntpdate.server.ip | 127.0.0.1 | — | NTP server IP |
engine.ntpdate.sync | false | — | Whether to perform NTP sync at boot |
Stream / CEP
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.stream.processor | DIRECT | — | Stream processing mode (DIRECT/BUFFERED) |
engine.cep.server.ip | 127.0.0.1 | ${PP_CEP_HOST:127.0.0.1} | CEP server IP |
engine.cep.server.port | 7400 | ${PP_CEP_CONNECT_PORT:7400} | CEP server port (auto-aligned to protocol: http→7400, https→7401) |
engine.cep.server.protocol | http | ${PP_CEP_PROTOCOL:http} | CEP protocol. Default http for internal communication; https for distributed/containerized |
engine.cep.server.api.key | (none) | ${PP_CEP_API_KEY} | For the CEPClient X-API-Key header. Must match the CEP server's cep.api.key |
Streaming Message Delivery
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.streaming.messaging.warning.ms | 5000 | — | Message delivery delay warning (ms) |
engine.streaming.messaging.timeout.ms | 10000 | — | Message delivery timeout (ms) |
engine.streaming.messaging.timeout.store.type | FILE_QUEUE | — | Storage method for timed-out messages (FILE_QUEUE/MEMORY) |
engine.streaming.messaging.timeout.recovery.type | DB | — | Recovery source for timed-out messages (DB/FILE) |
Job Worker Threads
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.job.thread.asset | 8 | — | Number of asset job threads |
engine.job.thread.point | 8 | — | Number of point job threads |
DDS
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.dds.enabled | true | — | Global DDS enable |
engine.dds.data.tag.enabled | true | — | Publish tag data to DDS |
engine.dds.data.asset.enabled | true | — | Publish asset data to DDS |
Anomaly Detection / Dataflow
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.data.anomaly.enabled | false | — | Enable anomaly detection (external service integration) |
engine.data.anomaly.url | https://127.0.0.1:8970 | — | Anomaly detection service URL |
engine.dataflow.class | plantpulse.core.engine.pipeline.dataflow.BaseDataFlow | — | Dataflow implementation class |
engine.dataflow.server-timestamp.override | false | — | Whether to force server timestamp (true: collection time, false: original) |
engine.dataflow.negative-timestamp.override | true | — | Whether to correct negative timestamps |
engine.factory.model.updater.class | plantpulse.core.engine.model.impl.DefaultFactoryMetaModelUpdater | — | Factory metamodel updater |
engine.event.handler.class | plantpulse.core.engine.event.DefaultEventHanlder | — | Event handler implementation (the class name typo Hanlder is kept exactly as the actual class name) |
engine.javagc.enabled | false | — | Whether to allow explicit System.gc() calls |
Flow Engine
| Property | Default | Substitution | Description |
|---|---|---|---|
flow.debug.execution.log | false | — | Node execution INFO logging. Set true only for debugging sessions |
flow.stats.prune.interval.ms | 86400000 | — | Counter cleanup interval (ms, 24 hours). Cassandra counter tables can't use default_time_to_live, so cleanup happens at the application level |
flow.stats.prune.hourly.days | 30 | — | Retention days for hour_bucket of tm_flow_stats_hourly |
flow.stats.prune.daily.days | 90 | — | Retention days for day_bucket of tm_flow_stats_daily |
flow.health.check.interval.ms | 600000 | — | Flow health check interval (ms, 10 min). ERROR if errors > 0 in the current time bucket, otherwise NORMAL (KST-based) |
flow.webhook.api.key | dev-webhook-19ba7a54-1132-49f2-967e-3c6f5cd989a0 | ${PP_FLOW_WEBHOOK_API_KEY:...} | Flow engine external webhook authentication |
Cassandra tables used by the Flow health check: tm_flow_health_status (flow_id, hour_bucket — current-time accumulator), tm_flow_health_status_10m (flow_id, min10_bucket — 10-minute time series).
Edge Health Check
| Property | Default | Substitution | Description |
|---|---|---|---|
edge.health.check.interval.ms | 600000 | — | Edge health check interval (ms, 10 min) |
The determination source is the _ON / _NORMAL flags in the Cassandra tm_edge_status payload. There is no RT REST call, so load is 0 and it finishes within a second. Tables: tm_edge_health_status (edge_id, hour_bucket), tm_edge_health_status_10m (edge_id, min10_bucket).
Authentication / Security
| Property | Default | Substitution | Description |
|---|---|---|---|
api.key | (none) | ${PP_API_KEY} | Platform-internal API key (server ↔ edge agent authentication) |
engine.api.v5.auth.bruteforce.enabled | true | — | V5 API auth brute-force defense |
engine.api.v5.auth.bruteforce.ip_limit | 30 | — | Attempt limit per IP |
engine.api.v5.auth.bruteforce.user_limit | 10 | — | Attempt limit per user |
engine.api.v5.auth.bruteforce.window.seconds | 60 | — | Counting window (seconds) |
engine.api.v5.trusted_proxies | (empty) | — | Trusted proxy IP allowlist. Only when set, if remoteAddr is in this list, the first IP in X-Forwarded-For is used as the client IP. If empty, only getRemoteAddr() is used (fail-safe) |
engine.session.bruteforce.enabled | true | — | /login/login browser session brute-force defense |
engine.session.bruteforce.ip_limit | 30 | — | Attempt limit per IP |
engine.session.bruteforce.user_limit | 10 | — | Attempt limit per user |
engine.session.bruteforce.window.seconds | 300 | — | Counting window (seconds) |
The session brute-force counter is Caffeine in-memory — multi-node sharing (Redis) is not implemented.
API Token
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.api.token.cleanup.enabled | true | — | Cleanup of expired (expires_at < now) / revoked (revoked_at IS NOT NULL) tokens |
engine.api.token.cleanup.retention_days | 30 | — | Hard delete from mm_token after this period elapses. Runs once every 24 hours (first run 60 seconds after boot) |
engine.api.token.default_ttl_days | 90 | — | Default expiration (days) for newly issued tokens. 0 = permanent token. If the issuance request supplies ttl_days, that value takes priority → API Token Issuance |
Existing tokens whose expires_at is NULL remain permanently active (backward compatibility).
plantpulse-storage.properties
Path: plantpulse-server/config/plantpulse-storage.properties · Template: template/plantpulse-storage.properties
Manages connections to the metastore (PostgreSQL) · cache (Valkey) · time series (Cassandra) · TSE · Data Gateway, along with storage policy.
⚠️ This file actually contains typo'd keys. 3 keys are spelled differently from what the code reads — be sure to read the Typo Key Warning section.
PostgreSQL Metastore
| Property | Default | Substitution | Description |
|---|---|---|---|
metastore.driver | org.postgresql.Driver | — | JDBC driver class |
metastore.url | jdbc:postgresql://... | ${PP_POSTGRES_HOST} · ${PP_POSTGRES_PORT} · ${PP_DB_NAME} | Metastore JDBC URL. Includes the ?characterEncoding=UTF-8&reWriteBatchedInserts=true query string |
metastore.user | — | ${PP_PG_USER} | Metastore account |
metastore.password | — | ${PP_PG_PASSWORD} | Metastore password |
Cache (Valkey / Redis-compatible)
| Property | Default | Substitution | Description |
|---|---|---|---|
cache.host | — | ${PP_REDIS_HOST} | Cache host |
cache.port | — | ${PP_REDIS_PORT} | Cache port (internal plaintext Valkey backend) |
cache.password | — | ${PP_REDIS_PASSWORD} | Cache password |
cache.ssl.enabled | false | — | Cache TLS. Default redis:// for internal clients |
cache.ssl.truststore.location | /var/security/plantpulse/master/master.truststore.p12 | — | Truststore path |
cache.ssl.truststore.password | kopens123! | ${PP_TLS_TRUSTSTORE_PASSWORD:kopens123!} | Truststore password |
cache.ssl.truststore.type | PKCS12 | — | Truststore type |
cache.ssl.endpoint.identification.enabled | false | — | Endpoint identification validation |
The single source of truth for key constants is InMemoryConfigKeys in plantpulse-inmemory.
TSE (TimeSeries Engine)
| Property | Default | Substitution | Description |
|---|---|---|---|
tse.protocol | http | ${PP_TSE_PROTOCOL:http} | TSE protocol. Defaults to plaintext http since it's internal communication (2026-08) |
tse.host | — | ${PP_TSE_HOST} | TSE host |
tse.port | 7800 | ${PP_TSE_CONNECT_PORT:7800} | TSE port. Defaults to plaintext listener 7800 |
tse.user | tse | ${PP_TSE_USER:tse} | TSE account |
tse.password | tse123! | ${PP_TSE_PASSWORD:tse123!} | TSE password |
Cassandra Time Series Storage
| Property | Default | Substitution | Description |
|---|---|---|---|
storage.db_type | CASSANDRA | — | DB kind (template comment still says CASSANDRA/DSE/SCYLLADB, but currently Cassandra-5.0-only) |
storage.db_version | 5.0 | — | DB version (determines driver/feature compatibility) |
storage.host | — | ${PP_CASSANDRA_HOST} | Contact point host |
storage.port | 9042 | ${PP_STORAGE_PORT:9042} | Native protocol port |
storage.keyspace | — | ${PP_KEYSPACE} | Keyspace to use |
storage.user | — | ${PP_CASSANDRA_USER} | Connection account |
storage.password | cassandra | ${PP_STORAGE_PASSWORD:cassandra} | Password. PP_STORAGE_PASSWORD is not exported by env.sh → default applies |
storage.durable_writes | true | — | Whether to use the commit log (⚠️ no code reads this — see below) |
storage.append_columns | [] | — | Auto-add user-defined columns |
storage.replication | { 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1 } | — | Replication strategy (single-node default) |
storage.data.disk.name | — | ${PP_DATA_DISK_NAME} | Data disk name |
TTL (unit: days)
| Property | Default | Description |
|---|---|---|
storage.tag.point.ttl | 62 | Tag time series original |
storage.tag.point.map.ttl | 1 | Tag point map |
storage.tag.point.sampling.ttl | 93 | Tag sampling |
storage.tag.point.snapshot.ttl | 93 | Tag snapshot |
storage.tag.point.aggregation.ttl | 93 | Tag aggregation |
storage.tag.point.archive.ttl | 365 | Tag archive (long-term retention) |
storage.tag.blob.ttl | 93 | Tag BLOB |
storage.asset.data.ttl | 10 | Asset data |
storage.asset.data.sampling.ttl | 31 | Asset sampling |
Asset Snapshot Interval
| Property | Default | Description |
|---|---|---|
storage.asset.data.snapshot.interval | 10 SECONDS | Records every asset's tag map (data_map) into the asset_data series (data/second/timestamp + minute/hour/day boundaries) before each cycle |
The template comment states the rationale: at 1 SECONDS, with 121 assets, it was about 22K writes/min (roughly 4% of all Cassandra writes) — too dense. Since the tag original (per second) is fully preserved in tm_tag_point/TSE, the asset-view snapshot interval was lowered to 10s (matching the code default) — writes −90%, while retaining recording completeness.
This value becomes the
ASSET_DATA_JOBinterval for the scheduler. It must be in"<n> <unit>"two-token format — see the scheduler architecture for the rules.
Internal Metastore Sync (Experimental)
| Property | Default | Description |
|---|---|---|
storage.internal.metastore.enabled | false | Enable internal metastore sync |
storage.internal.metastore.thread.count | 4 | Number of sync worker threads |
storage.internal.metastore.queue.size | 12000000 | Sync queue size |
storage.internal.metastore.point.retation.days | 10 | Point retention days (⚠️ typo'd key — see below) |
Row Cache
| Property | Default | Description |
|---|---|---|
storage.asset.data.row.cache.size | NONE | Asset data row cache (NONE/ROWS_ONLY/ALL) — ⚠️ no code reads this |
storage.tag.point.row.cache.size | NONE | Tag point row cache — ⚠️ no code reads this |
Compaction
| Property | Default | Description |
|---|---|---|
storage.table.compaction.strategy | UCS | Default strategy (TWCS: TimeWindow, UCS: Unified). Read by CassandraCreateDAO (code fallback is TWCS) |
storage.table.compaction.strategy.twcs.window_size | 1 | TWCS window size — ⚠️ no code reads this |
storage.table.compaction.strategy.twcs.window_unit | DAYS | TWCS window unit — ⚠️ no code reads this |
storage.table.compaction.strategy.ucs.scailing_parameter | T8 | UCS scaling parameter (T8 = tiered 8 levels). Key spelling scailing matches the code |
storage.table.compaction.strategy.ucs.min_sstable_size | 128MiB | UCS minimum sstable size (code fallback 100MiB) |
storage.table.compaction.strategy.ucs.target_sstable_size | 512MiB | UCS target sstable size |
storage.table.compaction.strategy.ucs.base_shard_count | 8 | UCS base shard count |
storage.table.compaction.strategy.ucs.max_sstables_to_compact | 6 | Maximum sstables to compact at once |
storage.table.compaction.strategy.ucs.sstable_growth | 0.7 | UCS sstable growth ratio |
Compression / Query Limits / Backup / Data Gateway
| Property | Default | Substitution | Description |
|---|---|---|---|
storage.compresion.zstd.level | 3 | — | zstd compression level (1~22) — ⚠️ typo'd key, see below |
storage.tag.point.select.limit.size | 1000000 | — | Max rows for a single tag point select |
storage.doamin.model.changed.to.metastore.backup | true | — | Metastore backup on domain model change — ⚠️ typo'd key + no code reads this |
data.gateway.protocol | http | ${PP_DATA_GATEWAY_PROTOCOL:http} | Data Gateway protocol |
data.gateway.host | 127.0.0.1 | ${PP_DATA_GATEWAY_HOST:127.0.0.1} | Data Gateway host (assumes local co-location) |
data.gateway.port | 5500 | ${PP_DATA_GATEWAY_CONNECT_PORT:5500} | Data Gateway port (code fallback 5501) |
data.gateway.api.key | — | ${PP_DATA_GATEWAY_API_KEY} | Data Gateway API key |
⚠️ Typo'd Keys — Editing Them Has No Effect
The 3 keys below actually exist in the template but are spelled differently from what the code reads. If an administrator changes the value on these lines, it is silently ignored and the code default applies.
| Key in template (typo) | Key code actually reads | Code default | Read location |
|---|---|---|---|
storage.compresion.zstd.level | storage.compression.zstd.level | 3 | StorageConfig → CassandraCreateDAO |
storage.internal.metastore.point.retation.days | storage.internal.metastore.point.retention.days | 10 | StorageConfig → PointDAO |
storage.doamin.model.changed.to.metastore.backup | none (no code reads it even with correct spelling) | — | — |
To actually change the value, add the correctly-spelled key to the same file. For example, to raise the zstd level to 6, you must add
storage.compression.zstd.level=6(this means adding a new correctly-spelled line, not fixing the typo'd one). Leaving the typo'd line in place is harmless, but the real fix is to correct the template itself.No code reads
storage.doamin.model.changed.to.metastore.backupunder either spelling. Backup on domain model change is not handled by this key but byMetastoreBackupDeployer(cron0 0 12 1 1/1 ? *, 1st of every month at 12:00) — see the scheduler architecture.
Storage Keys Not in the Template but Read by Code
| Property | Code Default | Description |
|---|---|---|
storage.compression.zstd.type | ZstdCompressor | Compressor class name |
Storage Template Keys with No Reading Code (editing has no effect)
storage.durable_writes · storage.asset.data.row.cache.size · storage.tag.point.row.cache.size · storage.table.compaction.strategy.twcs.window_size · storage.table.compaction.strategy.twcs.window_unit (+ the 3 typo'd keys above)
Even changing the two TWCS-related keys to storage.table.compaction.strategy=TWCS will not control window size/unit.
plantpulse-mq.properties
Path: plantpulse-server/config/plantpulse-mq.properties · Template: template/plantpulse-mq.properties
Manages Kafka / MQTT broker connections and DDS publish topics. The code-side SSOT is MqConfig (connection) and UNSTopicDefaults (topics).
Broker Connection
| Property | Default | Substitution | Description |
|---|---|---|---|
mq.host | — | ${PP_KAFKA_HOST} | Broker host (shared by Kafka/MQTT) |
mq.user | — | ${PP_MQ_USER} | Broker admin account |
mq.password | — | ${PP_MQ_PASSWORD} | Broker password |
mq.kafka.port | 9092 | ${PP_KAFKA_PORT:9092} | Kafka port |
mq.kafka.security.protocol | SASL_PLAINTEXT | — | Kafka security protocol |
mq.ssl.truststore.location | /var/security/plantpulse/master/master.truststore.p12 | — | Messaging TLS truststore (MQTT + optional Kafka TLS) |
mq.ssl.truststore.password | kopens123! | ${PP_TLS_TRUSTSTORE_PASSWORD:kopens123!} | Truststore password |
mq.ssl.truststore.type | PKCS12 | — | Truststore type |
mq.ssl.endpoint.identification.algorithm | (empty) | — | Hostname verification algorithm (empty = no verification) |
mq.mqtt.port | — | ${PP_MQTT_TLS_PORT} | MQTT connection port |
mq.mqtt.ssl.enabled | true | — | Use MQTT TLS |
Beyond the keys above, MqConfig also reads Kafka-specific truststore overrides mq.kafka.ssl.truststore.location / .password / .type and mq.kafka.ssl.endpoint.identification.algorithm (not present in the template; mq.ssl.* is used if absent).
DDS Topics
14 Kafka types (mq.dds.kafka.topic.*) + 14 MQTT UNS types (mq.dds.mqtt.topic.*) + 2 SparkplugB types (mq.dds.mqtt.sparkplugb.*).
The full set of keys and defaults is documented under Messaging Architecture → DDS Publish Topics. Topic values are part of the Kafka/MQTT wire contract and UNSTopicDefaultsContractTest freezes the literals, so arbitrary changes are prohibited.
${PP_TOPIC_PREFIX} simultaneously determines the Kafka topic prefix (pp-), the root of the MQTT UNS path (pp/), and the SparkplugB group ID.
plantpulse-mail.properties
Path: plantpulse-server/config/plantpulse-mail.properties · Template: template/plantpulse-mail.properties
SMTP/SMS settings for sending alarms/reports.
| Property | Default | Substitution | Description |
|---|---|---|---|
mail.send.enabled | false | ${PP_MAIL_SEND_ENABLED:false} | Master send gate. Unless true, all mail sending logs a WARN only and is skipped (no SMTP attempt is made at all). Site-specific opt-in |
mail.smtp.host | smtp-relay.brevo.com | ${PP_MAIL_SMTP_HOST:...} | SMTP host |
mail.smtp.port | 587 | ${PP_MAIL_SMTP_PORT:587} | SMTP port (25:plain, 465:SSL, 587:STARTTLS) |
mail.smtp.auth | true | ${PP_MAIL_SMTP_AUTH:true} | Whether SMTP auth is used |
mail.smtp.user | b2df89001@smtp-brevo.com | ${PP_MAIL_SMTP_USER:...} | SMTP sender account |
mail.smtp.password | bskHIVJqcFQaFtb | ${PP_MAIL_SMTP_PASSWORD:...} | SMTP password (dev default — replace before delivery) |
mail.smtp.starttls.enable | true | ${PP_MAIL_SMTP_STARTTLS:true} | Whether to use STARTTLS |
sms.sender.class | plantpulse.cep.listener.alarm.sms.DefaultSMSSender | — | SMS send implementation class (CEP listener) |
diagnostic.log.email | diag@kopens.com | ${PP_DIAGNOSTIC_EMAIL:diag@kopens.com} | Diagnostic notification recipient email — ⚠️ no code reads this |
PP_MAIL_SMTP_*is not exported byenv.sh. This template (the Brevo relay) is the SSOT, and overriding via environment variable silently breaks the login combination. Site overrides belong inenv.local.sh. See the substitution section for background.
plantpulse-ai.properties
Path: plantpulse-server/config/plantpulse-ai.properties · Template: template/plantpulse-ai.properties
Manages the OpenAI-compatible AI gateway and AI feature flags. On the code side, plantpulse.core.service.ai.AIConfig reads it via the same external configuration chain.
Gateway
| Property | Default | Substitution | Description |
|---|---|---|---|
ai.openai.enabled | false | ${PP_AI_OPENAI_ENABLED:false} | Master feature flag. If false, the AI UI/endpoints are hidden and disabled |
ai.openai.base.url | https://api.openai.com | ${PP_AI_OPENAI_BASE_URL:...} | OpenAI-compatible gateway. No trailing slash |
ai.openai.chat.path | /v1/chat/completions | ${PP_AI_OPENAI_CHAT_PATH:...} | chat completions path |
ai.openai.responses.path | /v1/responses | ${PP_AI_OPENAI_RESPONSES_PATH:...} | responses path |
ai.openai.api.key | (empty) | ${PP_AI_OPENAI_API_KEY:} | LiteLLM virtual key / proxy key. No repository default |
ai.openai.model | gpt-5-mini | ${PP_AI_OPENAI_MODEL:gpt-5-mini} | Model name exposed by LiteLLM config.yaml |
ai.openai.timeout.ms | 15000 | — | Request timeout (ms) |
ai.openai.max.output.tokens | 1200 | — | Max output tokens |
ai.openai.temperature | -1 | — | Sampling temperature. If negative, the parameter is omitted from the request body entirely |
⚠️
ai.openai.temperaturepitfall. The gpt-5 family (gpt-5-mini,gpt-5.5, etc.) doesn't support temperature, and sending anything other than the default of 1 causes a400 Unsupported value. So it must be left negative (an omission sentinel). The template comment's mention of "default 0.1" refers to the LiteLLM/vLLM gateway (gpt-4o, etc.); the current template's actual value is-1.
Operations Assistant
| Property | Default | Description |
|---|---|---|
ai.alarm.rca.enabled | true | Alarm root cause analysis |
ai.alarm.eql.enabled | true | Alarm EQL assistance |
ai.uns.topic.enabled | true | UNS topic assist |
ai.result.audit.enabled | true | AI result audit |
ai.similar.incident.enabled | true | Similar-incident recommendation |
ai.similar.incident.max.items | 5 | Max recommendation count |
ai.similar.incident.min.score | 40 | Minimum recommendation score |
The 3 ai.diagnostic.summary.* keys (diagnostic log AI summary) and the 3 ai.operations.report.* keys (AI operations
report) were removed from the product on 2026-08-25. Since the screens, JS, and
controllers were removed together, restoring the values will not bring the feature back.
It's harmless if these keys remain in the configuration file after an upgrade — no code reads them, so they're simply ignored. Feel free to delete them if you want to tidy up.
MCP / Chat
| Property | Default | Substitution | Description |
|---|---|---|---|
ai.mcp.enabled | true | ${PP_AI_MCP_ENABLED:true} | MCP (AI chat-ops endpoint) |
ai.mcp.allowed.origins | (empty) | ${PP_AI_MCP_ALLOWED_ORIGINS:} | List of allowed MCP Origins |
ai.chat.enabled | false | ${PP_AI_CHAT_ENABLED:false} | server-web console AI chat drawer. Only works when the ai.openai.* gateway is on |
Other Templates
Beyond the 5 above, template/ contains application/infrastructure configuration templates. The single source of truth for the deployment path mapping is ConfigTemplates.java.
| Template | Deployment Path | Purpose |
|---|---|---|
plantpulse-batch.properties | /plantpulse-batch/config/ | Batch (time series sync/aggregation) |
plantpulse-cep.properties | /plantpulse-cep/config/ | CEP engine |
plantpulse-jdbc.properties | /plantpulse-data-gateway/config/ and /plantpulse-sql/config/ | JDBC integration configuration (both modules share the same template) |
plantpulse-data-gateway.properties | /plantpulse-data-gateway/config/ | Data Gateway behavior |
plantpulse-warehouse.properties | /plantpulse-warehouse/config/ | Cassandra → Iceberg(S3) archive |
plantpulse-monitor.properties | /plantpulse-monitor/config/ | Monitor (JMX collection) |
plantpulse-plugin-opcua.properties | /plantpulse-plugin/opc-ua/config/ | OPC UA server plugin |
plantpulse-plugin-aas.properties | /plantpulse-plugin/aasx-server/config/ | AAS V3 (aasx-server) plugin |
kafka.properties | /plantpulse-messaging/kafka/config/kafka.properties | Kafka (KRaft) broker |
kafka-jaas.conf | /plantpulse-messaging/kafka/config/jaas.conf | Kafka JAAS |
hivemq.xml | /plantpulse-messaging/mqtt/conf/config.xml | HiveMQ broker |
plantpulse-mq-auth.properties | /plantpulse-messaging/mqtt/conf/auth.properties | HiveMQ auth source |
hive-auth.properties | /plantpulse-analytics/spark/conf/ | Hive/analytics account auth source |
plantpulse-timeseries-engine.conf | /plantpulse-timeseries/engine/conf/ (2 locations) | TSE |
valkey.conf · postgresql.conf · pg_hba.conf · cassandra.yaml · jvm-server.options | /plantpulse-storage/... | Storage |
spark-env.sh · spark-defaults.conf · hive-site.xml · kyuubi-defaults.conf · gravitino-iceberg-rest-server.conf | /plantpulse-analytics/... | Analytics |
workflow.yaml · application.yaml | /plantpulse-workflow/... | Temporal / Kestra |
plantpulse-startup.properties | (used by startup itself) | Data lake health-check connection info at boot |
plantpulse-sql.propertiesdoes not exist. The SQL module receivesplantpulse-jdbc.properties.plantpulse-websocket.propertiesalso does not exist. There is no WebSocket/STOMP-family configuration file.template/cluster/is a worker-node-only override — if you edit the master template, this must be synced too.
plantpulse-batch.properties
Composed of connection blocks (platform/metastore/cache/TSE/Kafka/Cassandra/Spark) plus batch behavior settings. Note that connection key names differ from storage/jdbc — e.g., Cassandra is storage.db.host / storage.db.port / storage.db.keyspace (server uses storage.host / storage.port / storage.keyspace).
| Property | Default | Description |
|---|---|---|
platform.host | ${PP_MASTER_IP} | Platform (server-web) host |
mq.port | 9092 | Kafka port (literal — not substituted) |
kafka.topic | pp-batch | Batch ingestion topic |
kafka.group.id | PP-BATCH-GROUP | Batch consumer group |
batch.timer_schedule_delay | 5000 | Batch timer interval (ms) |
batch.thread_pool_batch_thread_size | 4 | Number of batch worker threads |
batch.buffer_drain_limit_size | 10000 | Max items flushed at once |
batch.sync.inmemory.enabled | false | In-memory sync |
batch.sync.inmemory.ttl.minute | 10 | In-memory sync TTL (minutes) |
batch.sync.tse.enabled | true | Distribute to TSE |
batch.sync.external.db.enabled | false | Sync to external DB |
external.db.name | TIMESCALEDB | External DB type. The INFLUXDB/QUESTDB blocks are commented out in the template |
analytics.url | jdbc:spark://${PP_SPARK_HOST}:${PP_KYUUBI_PORT}/default | Spark Thrift JDBC |
plantpulse-cep.properties
| Property | Default | Description |
|---|---|---|
cep.ha.enabled | true | Redis-based HA kill switch (preserves deployment/named window/single-key table state across restarts) |
cep.ha.snapshot.interval_ms | 1000 | Snapshot interval (non-blocking background off the event thread) |
cep.ha.snapshot.named_windows | true | Named window snapshot |
cep.ha.snapshot.tables | true | Table snapshot |
cep.async.core_pool_size | 32 | Async core pool |
cep.async.max_pool_size | 256 | Async max pool |
cep.async.queue_capacity | 50000 | Async queue |
cep.async.keep_alive_seconds | 60 | Thread keep-alive |
cep.async.shutdown_timeout_seconds | 30 | Graceful shutdown wait |
cep.consumer.thread_count | 16 | Consumer threads |
cep.stream.shard_count | 16 | Number of stream shards |
cep.stream.drain_limit_size | 1000 | Drain limit |
cep.stream.drain_warn_ms | 1000 | Drain warning (ms) |
cep.monitor.initial_delay_seconds | 5 | Monitor initial delay |
cep.monitor.interval_seconds | 1 | Monitor interval |
cep.monitor.store_interval_seconds | 60 | Cassandra storage interval for per-EQL-statement metrics |
cep.monitor.statement_metrics_enabled | true | Collect per-statement metrics |
cep.performance.cpu_warn_threshold_ns | 1000000000 | CPU warning threshold (ns) |
cep.performance.wall_warn_threshold_ns | 1000000000 | Wall warning threshold (ns) |
cep.api.key | ${PP_CEP_API_KEY} | EQL management endpoint authentication (X-API-Key) |
cep.api.max_eql_text_length | 100000 | EQL text max length (DOS defense) |
cep.storage.request_timeout_seconds | 10 | Cassandra request timeout |
cep.storage.max_inflight_writes | 256 | In-flight write cap |
cep.storage.connect_max_retries | 3 | Connection retry |
cep.storage.ttl_seconds | 864000 | Metrics TTL (seconds, 10 days) |
cep.stream.pending_idle_threshold_ms | 60000 | Pending idle threshold |
cep.stream.cleaner_initial_delay_ms | 60000 | Cleaner initial delay |
cep.stream.cleaner_interval_ms | 10000 | Cleaner interval |
cep.stream.max_clean_duration_ms | 30000 | Clean max duration |
cep.stream.cleaner_max_claim_count | 5000 | Claim max count |
cep.stream.ack_chunk_size | 500 | ACK chunk size |
There is a rationale behind 60 of cep.monitor.store_interval_seconds — writes scale with the number of statements (asset × EMS/alarm rules, dev measured at 1,263), which produced 7.6K rows/min at 10s. Since this is used only for on-demand queries, it was relaxed to 60s (−83%).
The HA-dedicated Redis connection (cep.ha.redis.host/port/password) is commented out in the template — only enable it when using a Redis instance different from the cache.
plantpulse-jdbc.properties
Unified connection configuration for external JDBC clients (SQL module · Data Gateway). Prefixes are jdbc.<tech> (postgres/cassandra/spark/tse).
| Property | Value | Description |
|---|---|---|
jdbc.postgres.driver | org.postgresql.Driver | META / PQL |
jdbc.postgres.url | jdbc:postgresql://${PP_POSTGRES_HOST}:${PP_POSTGRES_PORT}/${PP_DB_NAME}?characterEncoding=UTF-8 | |
jdbc.cassandra.driver | plantpulse.cassandra.jdbc.CassandraDriver | OLTP time series (CQL). Driver value is for config consistency (functionally unused) |
jdbc.cassandra.url | jdbc:cassandra://${PP_CASSANDRA_HOST}:${PP_CASSANDRA_PORT}/${PP_SCHEME} | |
jdbc.tse.driver | plantpulse.timeseries.tql.TQLDriver | OTAP / TQL |
jdbc.tse.url | jdbc:ts://${PP_TSE_HOST}:${PP_TSE_PORT}/${PP_SCHEME} | ⚠️ fixed to plain http (ts/7800) |
jdbc.spark.driver | plantpulse.spark.jdbc.SparkSQLDriver | OLAP analytics |
jdbc.spark.url | jdbc:spark://${PP_SPARK_HOST}:${PP_KYUUBI_PORT}/default/${PP_SCHEME} | |
data.gateway.api.key | ${PP_DATA_GATEWAY_API_KEY} | For JDBC call authentication |
⚠️ Do not change
jdbc.tse.urlto https (tss/7801). The TQL JDBC driver uses the plaintext TSE listener (7800). TSE clients default globally to plaintext http/7800 (2026-08, env.sh TSE section). If pinned to tss/7801, the JDBC connection fails → data-gateway fails to boot → all/api/v1/*return 404 → this cascades into 404s for the analytics catalog. That's why it's pinned tots/PP_TSE_PORT.
jdbc.spark.usernameis an analytics-only account,${PP_ANALYTICS_USER}. It is separate from the Kyuubi internal metastore account (PP_HIVE_*). The optional keysjdbc.<tech>.pool.initial|min|max(postgres/spark/tse) andjdbc.<tech>.query.timeout|fetch-sizecan also be used (seeplantpulse-jdbc/docs/configuration.mdfor the full reference).jdbc.cassandra.local-datacenteris commented out in the template and defaults todatacenter1if not set.
plantpulse-data-gateway.properties
| Property | Default | Description |
|---|---|---|
data.gateway.api.key | ${PP_DATA_GATEWAY_API_KEY:ADFA-URDV-QWED-1234} | API auth key (required). Replace the dev default before delivery |
data.gateway.rate.limit | 1000 | Max requests per second (based on IP + API key) |
data.gateway.slow.query.ms | 10000 | Slow query warning threshold (ms) |
data.gateway.slow.update.ms | 2000 | Slow update warning threshold (ms) |
data.gateway.async.timeout.ms | 60000 | Async timeout (ms) |
plantpulse-monitor.properties
A collection of JMX/management endpoints for each service, based on a single node (type=MASTER). This is the only file that uses underscores (_) instead of dots (.) in keys.
| Property | Default | Description |
|---|---|---|
type | MASTER | Single node or cluster master |
db_type | CASSANDRA | DB type being monitored |
master / host | ${PP_MASTER_IP} / ${PP_HOST_IP} | Master / own host |
db_host · db_port · db_keyspace · db_username · db_password · db_replication_factor | ${PP_CASSANDRA_*} · 9042 · ${PP_SCHEME} · 1 | Cassandra connection |
tse_host · tse_port · tse_protocol · tse_username · tse_password | ${PP_TSE_*} · 7801 · https · tse · tse123! | TSE connection (metric ingestion) |
kafka_jmx_uri | service:jmx:rmi:///jndi/rmi://${PP_KAFKA_HOST}:7299/jmxrmi | Kafka JMX |
mqtt_jmx_uri | ...://${PP_MQTT_HOST}:7279/jmxrmi | HiveMQ JMX |
cep_jmx_uri | ...://${PP_HOST_IP}:6499/jmxrmi | CEP JMX |
datagateway_jmx_uri | ...://${PP_HOST_IP}:7499/jmxrmi | Data Gateway JMX |
sql_jmx_uri | ...://${PP_HOST_IP}:7599/jmxrmi | SQL JMX |
tse_jmx_uri | ...://${PP_HOST_IP}:7899/jmxrmi | TSE JMX |
cassandra_jmx_uri | ...://${PP_CASSANDRA_HOST}:7199/jmxrmi | Cassandra JMX |
spark_jmx_uri | ...://${PP_SPARK_HOST}:10010/jmxrmi | Spark JMX |
redis_jmx_uri | redis://${PP_REDIS_HOST}:${PP_REDIS_PORT}?password=... | Valkey connection URI |
redis_ssl_truststore_location / _password / _type / redis_ssl_endpoint_identification_enabled | p12 · ${PP_TLS_TRUSTSTORE_PASSWORD:kopens123!} · PKCS12 · false | Valkey TLS |
postgres_jmx_uri | jdbc:postgresql://.../${PP_DB_NAME}?user=...&password=... | PostgreSQL connection URI |
JMX Port Summary: HiveMQ 7279, Kafka 7299, CEP 6499, Data Gateway 7499, SQL 7599, TSE 7899, Cassandra 7199, Spark 10010.
plantpulse-warehouse.properties
Cassandra → Iceberg(S3) archive batch. This is the only file where keys are UPPER_SNAKE_CASE (UPPER_SNAKE).
| Group | Key Keys | Default |
|---|---|---|
| Temporal | TEMPORAL_SERVER · TEMPORAL_TASK_QUEUE · TEMPORAL_WORKFLOW_ID · TEMPORAL_SCHEDULE_ID · OPTIMIZER_SCHEDULE_ID · OPTIMIZER_WORKFLOW_ID | ${PP_TEMPORAL_HOST}:${PP_TEMPORAL_PORT} · PP-S3-QUEUE · PP-S3-ARCHIVE-DAILY-WORKFLOW · PP-S3-ARCHIVER-DAILY · PP-S3-OPTIMIZER-WEEKLY · PP-S3-OPTIMIZER-WEEKLY-WORKFLOW |
| Schedule | DAILY_HOUR · DAILY_MINUTE · OPTIMIZER_DAY_OF_WEEK · OPTIMIZER_HOUR · OPTIMIZER_MINUTE · TIMEZONE | 2 · 20 · 0 (Sunday) · 3 · 0 · ${PP_TZ} |
| Execution mode | ARCHIVER_MODE | SHELL (or KESTRA) |
| Kestra | KESTRA_SERVER · KESTRA_NAMESPACE · KESTRA_TASK_ID · KESTRA_USER · KESTRA_PASS | ${PP_KESTRA_HOST}:${PP_KESTRA_PORT} · plantpulse · plantpulse-daily-s3-archiver · admin@plantpulse.io · (empty) |
| Scripts | S3_SCRIPT_DIR · S3_SCRIPT_NAME · S3_OPTIMIZER_SCRIPT_NAME | ${PP_HOME}/plantpulse-warehouse/s3 · archive.sh · optimize.sh |
| Spark | SPARK_APP_NAME · SPARK_MASTER · SPARK_JAR_PATH · SPARK_DRIVER_MEMORY · SPARK_EXECUTOR_MEMORY · SPARK_EXECUTOR_CORES · SPARK_NUM_EXECUTORS · ARCHIVER_PARALLELISM · SPARK_CHECKPOINT_DIR | PP-SPARK-S3-LAKEHOUSE · local[*] · .../plantpulse-warehouse-s3.jar · 2g · 4g · 4 · 3 · 4 · s3a://${PP_MINIO_BUCKET:-plantpulse}/spark-checkpoint/ |
| Source/Target | CASSANDRA_CATALOG · CASSANDRA_DATABASE · S3_BUCKET · ICEBERG_CATALOG · ICEBERG_DATABASE | cassandra · ${PP_SCHEME:pp} · ${PP_MINIO_BUCKET:-plantpulse} · spark_catalog · ${PP_SCHEME:pp} |
| Optimization | SNAPSHOT_RETENTION_DAYS · OPTIMIZER_MIN_INPUT_FILES · OPTIMIZER_TARGET_FILE_SIZE_BYTES | 3 · 10 · 134217728 (128MB) |
| Timeouts | ARCHIVER_WF_EXECUTION/RUN/TASK_TIMEOUT_HOURS · OPTIMIZER_WF_EXECUTION/RUN/TASK_TIMEOUT_HOURS · ACTIVITY_MAX_ATTEMPTS · ACTIVITY_TIMEOUT_HOURS · OPTIMIZER_ACTIVITY_TIMEOUT_HOURS · SCRIPT_TIMEOUT_SECONDS · OPTIMIZER_TIMEOUT_SECONDS · HEARTBEAT_INTERVAL_SECONDS | 12/12/12 · 8/7/12 · 3 · 12 · 8 · 21600(6h) · 25200(7h) · 60 |
| Web | WEB_SERVER_PORT | 9600 |
Messaging / Auth Source Templates
plantpulse-mqtt.properties — HiveMQ broker configuration.
| Property | Default |
|---|---|
mqtt.host · mqtt.user · mqtt.password | ${PP_MQTT_HOST} · ${PP_MQ_USER} · ${PP_MQ_PASSWORD} |
mqtt.port · mqtt.ssl.enabled | ${PP_MQTT_TLS_PORT} · true |
mqtt.websocket_port | 8888 |
mqtt.data_dir | /data1/pp-data/hivemq |
plantpulse-mq-auth.properties — the auth source that the HiveMQ security extension (FileAuthAuthenticator) points to via the HIVEMQ_AUTH_FILE environment variable. There are only two keys: mqtt.user / mqtt.password.
hive-auth.properties — read by plantpulse-hive-auth (PasswordAuthenticator). Uses the HIVE_AUTH_FILE environment variable or $PP_HOME/plantpulse-analytics/spark/conf/hive-auth.properties. What's unusual is that the key name itself is a placeholder.
${PP_HIVE_USER}=${PP_HIVE_PASSWORD}
${PP_ANALYTICS_USER}=${PP_ANALYTICS_PASSWORD}
kafka.properties — Kafka broker in KRaft mode. Key values: process.roles=broker,controller, node.id=1, num.partitions=4, 3 listener types (SASL_PLAINTEXT/CONTROLLER/SASL_SSL), sasl.enabled.mechanisms=PLAIN, super.users=User:admin, allow.everyone.if.no.acl.found=false, log.dirs=${PP_DATA_DIR}/kafka/kraft_combined_logs, log.retention.ms=3600000 (1 hour), message.max.bytes=157286400, default.replication.factor=1, auto.create.topics.enable=true.
⚠️
log.retention.ms=3600000= 1 hour. The premise is that Kafka acts as a buffer, while persistence is handled by Cassandra/TSE. If a consumer is stalled for more than 1 hour, data will be lost.
Plugin Templates
plantpulse-plugin-opcua.properties — PostgreSQL / Cassandra / Valkey / Kafka connections + OPC UA server endpoint.
| Property | Default | Description |
|---|---|---|
kafka.topic | pp-tag-point | ⚠️ literal — not a ${PP_TOPIC_PREFIX} substitution |
kafka.thread · kafka.group.id | 8 · opcua-plugin-group | |
opc.ua.server.domain · opc.ua.server.port | ${PP_HOST_IP} · 8007 | |
opc.ua.server.user · password · anonymous | opcua · 설치-시-변경 · false | |
opc.ua.server.tcp.port · tls.port | ${PP_OPCUA_TCP_PORT:11004} · ${PP_OPCUA_TLS_PORT:11005} | |
redis.thread · redis.ssl.enabled | 4 · false |
plantpulse-plugin-aas.properties — AAS V3 server + Kafka consumption configuration.
| Property | Default | Description |
|---|---|---|
aas.v3.port | ${PP_AAS_V3_PORT:8090} | V3 HTTP port |
kafka.topic | ${PP_TOPIC_PREFIX}-tag-point | Real-time tag point ingestion |
kafka.thread · kafka.poll.count | 8 · 1000 | |
group.id · auto.offset.reset · enable.auto.commit | pp-plugin-aas-server · earliest · false | |
session.timeout.ms · auto.commit.interval.ms | 30000 · 1000 | |
plugin.message.listener | plantpulse.plugin.opcua.messaging.KafkaPluginMessageListener |
BaSyx V2's registry/AAS ports are hardcoded in the code, not configurable —
REGISTRY_PORT=4800,AAS_PORT=4801inAASServer.java.
plantpulse-startup.properties
Connection info used by Checker for the data lake/health check at boot. Composed of platform.host · metastore.* · cache.* · tse.* · storage.db.* · analytics.* · object.* (MinIO) blocks. All values are env.sh substitutions and use the same key-naming convention (storage.db.*) as the batch template.
Engine Keys Not in the Template But Read by Code
EngineConfig also reads the keys below. Since they are not in the template (plantpulse-engine.properties), the code default applies as usual; to change a value, you must add a line directly to plantpulse-server/config/plantpulse-engine.properties.
Edge Clock / Status Polling
| Property | Code Default |
|---|---|
edge.clock.poll.term.sec | 60 |
edge.clock.skew.threshold.ms | 10000 |
edge.clock.rtt.max.ms | 2000 |
edge.clock.autosync.enabled | true |
edge.clock.autosync.cooldown.ms | 600000 |
edge.clock.warn.cooldown.ms | 600000 |
edge.clock.skip.warn.count | 5 |
edge.status.poll.seconds | 30 |
edge.status.poll.jitter.max.ms | 3000 |
edge.clock.poll.term.sec and edge.status.poll.seconds determine the scheduler job interval — see the Edge section of the scheduler architecture for value constraints.
DDS / Event Bus / Push / Buffer
| Property | Code Default |
|---|---|
engine.dds.queue.size | 80000 |
engine.dds.worker.threads | 4 |
engine.dds.latency.warn.ms | 1000 |
engine.eventbus.queue.capacity | 65536 |
engine.push.threads | 4 |
engine.push.queue.size | 10000 |
engine.storage.buffer.type | KAFKA |
engine.streaming.timeout.backup.queue.capacity | 12000000 |
The code default for
engine.dds.enabled·engine.dds.data.tag.enabled·engine.dds.data.asset.enabledisfalse. Because the template explicitly sets them totrue, they are only enabled in the deployed build — deleting these three lines silently turns DDS off.
Job Threads (not listed in the template)
| Property | Code Default |
|---|---|
engine.job.thread.oee | 10 |
engine.job.thread.ram | 5 |
engine.job.thread.ems | 5 |
engine.job.thread.system | 4 |
engine.job.asset.slow.threshold.ms | 20000 |
⚠️ The code default for
engine.job.thread.pointis4, but the template sets8(engine.job.thread.assetmatches8on both sides). Deleting the template line halves the value.
Flow Engine (not listed in the template)
| Property | Code Default |
|---|---|
flow.engine.enabled | true |
flow.executor.parallelism | 64 |
flow.executor.queue.capacity | 10000 |
flow.executor.queue.size | 10000 |
flow.executor.poll.timeout.ms | 50 |
flow.scheduler.pool.size | 4 |
flow.timer.pool.size | 2 |
flow.jdbc.trigger.pool.size | 2 |
flow.shutdown.timeout.sec | 5 |
flow.enabled.cache.ttl.ms | 5000 |
flow.webhook.auth.enabled | true |
Alarm / ISO Metrics / OEE
| Property | Code Default |
|---|---|
alarm.duplicate.check.minutes | 10 |
engine.oee.debounce.ms | 100 |
iso.oee.rolling.window.minutes | 10 |
iso.ems.rolling.window.minutes | 10 |
iso.ram.rolling.window.minutes | 1440 |
Anomaly Detection Details (Asset Point / System)
Meaningful only when enabled via engine.data.anomaly.enabled=true.
| Property | Code Default |
|---|---|
engine.data.anomaly.asset.point.interval | MINUTE |
engine.data.anomaly.asset.point.lookback.minutes | 14400 |
engine.data.anomaly.asset.point.recent.seconds | 600 |
engine.data.anomaly.asset.point.limit | 2100 |
engine.data.anomaly.asset.point.context.length | 2048 |
engine.data.anomaly.asset.point.prediction.length | 96 |
engine.data.anomaly.asset.point.score.history | 60 |
engine.data.anomaly.asset.point.threshold.k | 3.5 |
engine.data.anomaly.asset.point.threshold.min | 0.80 |
engine.data.anomaly.asset.point.threshold.max | 0.98 |
engine.data.anomaly.asset.point.topk.request | 5 |
engine.data.anomaly.asset.point.topn.report | 5 |
engine.data.anomaly.system.lookback.minutes | 1440 |
engine.data.anomaly.system.recent.seconds | 60 |
engine.data.anomaly.system.limit | 2100 |
engine.data.anomaly.system.context.length | 2048 |
engine.data.anomaly.system.pred.length | 96 |
engine.data.anomaly.system.threshold.min | 0.80 |
engine.data.anomaly.system.topk | 2 |
Settings That Do Not Exist
The names below appear in past documentation/external material but exist neither in the template nor in the code. Adding a setting under these names has no effect whatsoever.
| What Was Sought | Actual Location |
|---|---|
storage.postgresql.* | metastore.driver / metastore.url / metastore.user / metastore.password |
storage.redis.* | cache.host / cache.port / cache.password / cache.ssl.* |
storage.cassandra.* | storage.host / storage.port / storage.keyspace / storage.user / storage.password |
storage.tse.* | tse.protocol / tse.host / tse.port / tse.user / tse.password |
storage.ttl.realtime etc. | Per-table storage.tag.point.ttl / storage.asset.data.ttl etc. (TTL Section) |
storage.compression.enable / .algorithm | storage.compression.zstd.level / storage.compression.zstd.type (the template line is a typo — Typo Key Warning) |
storage.compaction.enable / .schedule | The storage.table.compaction.strategy family (Compaction Section) |
storage.metastore.* / storage.cache.* | None |
mq.type / mq.kafka.bootstrap.servers / mq.kafka.group.id | mq.host + mq.kafka.port. The consumer group is not a setting but a code constant (PP-KAFKA-POINT-GROUP etc., Messaging Architecture) |
mail.* outside of plantpulse-server/config/ | Only in plantpulse-mail.properties |
websocket.* / plantpulse-websocket.properties | None. STOMP/WebSocket was retired in 2026-07; browser push is SSE (/push/sse) — Messaging Architecture |
scheduler.enabled / scheduler.thread.count / scheduler.misfire.* / scheduler.history.* | None. Quartz configuration is at plantpulse-server-web/src/quartz.properties (WAR classpath) — Scheduler Architecture |
engine.ntp.enable / engine.ntp.server | engine.ntpdate.sync / engine.ntpdate.server.ip |
engine.cep.enable / engine.cep.host / engine.cep.port | engine.cep.server.ip / engine.cep.server.port / engine.cep.server.protocol |
engine.anomaly.* | engine.data.anomaly.* |
engine.dds.enable / engine.dds.topic.prefix | engine.dds.enabled, the topic prefix is in PP_TOPIC_PREFIX (mq template) |
engine.async.queue.size / engine.async.timeout / engine.pipeline.batch.size / engine.timeout.* / engine.dataflow.threads / engine.dataflow.buffer.size | None |
PP_STOMP_* / PP_WEBSOCKET_* env variables | Not present in env.sh |
Properties Containing Passwords
These values must be replaced before delivery. The dev plaintext defaults are placeholders in env.sh (or the template), and for production/customer delivery, either replace them with customer-specific values or override via Docker -e VAR=... / export.
| File | Keys |
|---|---|
plantpulse-storage.properties | metastore.password · cache.password · cache.ssl.truststore.password · storage.password · tse.password · data.gateway.api.key |
plantpulse-engine.properties | api.key · flow.webhook.api.key · engine.cep.server.api.key |
plantpulse-mq.properties | mq.password · mq.ssl.truststore.password |
plantpulse-mail.properties | mail.smtp.password |
plantpulse-ai.properties | ai.openai.api.key |
plantpulse-cep.properties | cache.password · storage.password · cep.api.key |
plantpulse-batch.properties | metastore.password · cache.password · tse.password · mq.password · storage.db.password · analytics.password · external.db.password |
plantpulse-jdbc.properties | cache.password · jdbc.postgres.password · jdbc.cassandra.password · jdbc.tse.password · jdbc.spark.password · data.gateway.api.key |
plantpulse-data-gateway.properties | data.gateway.api.key |
plantpulse-monitor.properties | db_password · tse_password · redis_jmx_uri (password in URL) · redis_ssl_truststore_password · postgres_jmx_uri (password in URL) |
plantpulse-mqtt.properties · plantpulse-mq-auth.properties | mqtt.password |
plantpulse-plugin-opcua.properties | postgres.password · cassandra.password · redis.password · kafka.password · opc.ua.server.password |
plantpulse-plugin-aas.properties | aas.db.password · cassandra.password · kafka.password |
kafka.properties | ssl.keystore.password · ssl.key.password · ssl.truststore.password |
hive-auth.properties | The entire value is the password |
API key policy:
PP_API_KEY·PP_DATA_GATEWAY_API_KEY·PP_FLOW_WEBHOOK_API_KEY·PP_CEP_API_KEYare left with a dev plaintext default fallback (${VAR:-<dev-default>}) — so dev/test can boot and deploy without injecting additional secrets. Do not change this to hard-fail (:?) —restart-server.shwould die instantly while sourcingenv.sh, causing deployment to fail.
Recommended Actions
# 설정 파일 권한 제한
chmod 600 /opt/kopens/plantpulse-platform/plantpulse-*/config/*.properties
# 사이트별 비밀값은 env.local.sh 로 분리 (env.sh 는 SSOT 유지)
vi /opt/kopens/plantpulse-platform/plantpulse-startup/env.local.sh
Do not commit secrets to the repository.