Property Reference
This document is regenerated from the physical configuration templates (
*.properties) being deployed. The authoritative source of the templates isplantpulse-datalake-cli/config/templates, and the host's/etc/kopens/confis bind-mounted to that location. Which template is rendered where is determined byconfig/render.map. The authoritative source for loading priority isplantpulse-core/src/plantpulse/core/engine/utils/PropertiesUtils.java.If keys have been added, deleted, or renamed, the template—not this document—is the authoritative source. Do not fill in this document manually; extract it again from the template. The "Default" values in each table are exactly as written in the template, and
${PP_XXX}are placeholders substituted with environment variable values during rendering.
Value Flow: Source to Destination
| Item | Location | Permissions | How to Change |
|---|---|---|---|
| Secrets — DB passwords · API keys | /etc/kopens/plantpulse-platform.env | 0600 | bin/passwd.sh (Change server account → Update sidecar → Re-render → Restart as one sequence) |
Node-specific values — PP_MASTER_IP · Kafka advertised address · Image tag pins | /etc/kopens/platform.node.env | 0600 | Edit directly, then bin/restart-datalake.sh |
| Settings — Ports · Image tags · Locale · Data paths | .env in repo (Commit .env.example only) | — | Edit directly, then restart |
«Lines» of Engine Settings — postgresql.conf parameters, Kafka retention, Aster TSDB heap | /etc/kopens/conf/<파일>.template | — | Edit template → pd config render → pd restart <서비스> |
pd start renders the template every time and overwrites the configuration location of each component. Even if you open and edit cassandra.yaml, postgresql.conf, or kafka.properties, they will revert to their original state upon the next startup.
The place to make changes is the template → How to change settings
Secret files are copied to app nodes during split installation, whereas node files must not be copied.
Merging them will leak one box's PP_MASTER_IP and image tag pins to other boxes.
Only one format is supported: VAR=값 — if export is appended, env_file: will fail to parse it.
Standard Template Change Procedure
Modify values on the host and reflect them into pd inside the container.
vi /etc/kopens/conf/<템플릿> # 1. 호스트의 템플릿을 고칩니다
docker exec plantpulse-datalake pd doctor # 2. 검사 (선택)
docker exec plantpulse-datalake pd config diff # 3. 무엇이 달라지는지 봅니다
docker exec plantpulse-datalake pd config render # 4. 실제로 씁니다
docker exec plantpulse-datalake pd restart <서비스> # 5. 재시작
docker exec plantpulse-datalake pd status # 6. 확인
pd config renderonly rewrites the configuration file and does not restart the service. Until a restart occurs, the new configuration exists only on disk while the old process remains active. This is why steps 4 and 5 are a single set.
Configuration File Loading Priority
Web applications (server / batch / cep / sql / data-gateway) search for configuration files in the following order. The implementation is PropertiesUtils.readExternalFirst().
- Directory specified as a
-Dpp.conf.dirsystem property (constantPropertiesUtils.CONF_DIR_SYSTEM_PROPERTY = "pp.conf.dir") ${catalina.base}/../config— Theconfig/directory of the module- Web app classpath (defaults included in WAR) — fallback
External files are read as UTF-8. If external configurations are loaded successfully, the following is printed in the startup log:
Properties loaded from external conf: <absolute-path>
If reading an external file fails, an error log is recorded, and it falls back to the classpath.
Logging configurations (log4j2.xml) are also externalized through the same chain (self-seeding — if no external file exists, WAR defaults are copied to the module config/log4j2.xml once and then used). When applied, Logging reconfigured from external conf: <absolute-path> is printed in the startup log. Changes to log levels are applied by modifying module config/log4j2.xml and restarting, which is preserved even after WAR redeployment. Refer to the configuration and deployment guide for details.
Caution: Do not directly modify internal web app files (
server/webapps/ROOT/WEB-INF/classes/, etc.) — they will disappear upon WAR redeployment.
${ENV:기본값} Substitution Occurs Twice
The ${PP_XXX} / ${PP_XXX:기본값} notation in templates is interpreted at two different points in time.
| Timing | Agent | Action |
|---|---|---|
| At Startup | pd config render | Substituted with sidecar (plantpulse-platform.env · platform.node.env) and container environment. Order: Environment variable → :기본값 → Preserve original. Nested placeholders are not supported |
| At Runtime | PropertiesUtils.resolveEnvPlaceholders | Re-interprets ${ENV_VAR[:default]} for all values in the loaded Properties |
In other words, any ${...} remaining after deployment can still be filled by the process's environment variables. This is why Docker -e VAR=... injection takes precedence over template defaults.
PP_* Variables Not in Sidecar (Template defaults apply)
The following variables are referenced by the template as ${PP_XXX:기본값} but are not defined in the sidecar file. Therefore, the template default is always used unless explicitly injected.
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_* was on this list but now does nothingProviding PP_AI_OPENAI_*, PP_AI_MCP_*, or PP_AI_CHAT_ENABLED will be silently ignored. The layer that read those values was removed on 2026-09-18 — see AI Configuration.
PP_STORAGE_PASSWORD · PP_METASTORE_PASSWORD were on this list but are «non-existent variables»Neither the template nor the sidecar references those names. The actual names are as follows:
| Item | Actual Variable |
|---|---|
| Time series storage (Aster TSDB) connection | PP_CASSANDRA_HOST · PP_CASSANDRA_PORT · PP_CASSANDRA_USER · 🔑 PP_CASSANDRA_PASSWORD |
| Metastore (PostgreSQL) connection | PP_POSTGRES_HOST · PP_POSTGRES_PORT · PP_PG_USER · 🔑 PP_PG_PASSWORD |
🔴 PP_MAIL_SMTP_* also now does nothing. The authoritative source for mail settings is pp.mm_config, and values are changed via the console (/config/index) or that table — Mail Configuration. This migration was made to ensure that values that must match as a set (host, port, authentication, account) are modified in one place only.
5 Types of Server Management Settings
There are 5 configuration files defined by the ConfigTemplates.SERVER_CONFIGS array and deployed as 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 |
These three files are read all at once by PropertiesLoader.load() (engine/mq/storage), and typed configuration beans EngineConfig · StorageConfig · MqConfig are reconstructed.
pp.mm_config is the authoritative sourceplantpulse-mail.properties and plantpulse-ai.properties were deleted on 2026-09-17, and the path that read old copies remaining in the external configuration directory and moved them to a table was completely removed on 2026-09-18. In other words, even if you create those files, nothing will be read.
The only paths for values are the console and PostgreSQL pp.mm_config. The authoritative source for key names and defaults is in the code's MailConfigKeys · AiConfigKeys; keys without a row in the table are read using those defaults — the key tables in the two sections below remain valid.
application.properties (Removed)
application.properties was removed in June 2026. There is no code that reads this file (0 usages across all repos), and existing items were migrated as follows:
- Console behavior settings such as theme / homepage → Console System > Configuration Management (PostgreSQL
mm_configtable) alarm.duplicate.check.minutes→plantpulse-engine.propertiesseries (read byEngineConfig)
plantpulse-engine.properties
Path: plantpulse-server/config/plantpulse-engine.properties · Template: template/plantpulse-engine.properties
Controls the core operation 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 processing limit per second (rate limit) |
engine.pipeline.queue.size | 1200000 | — | Pipeline queue size (backpressure limit) |
engine.pipeline.queue.o3.delay | 50 | — | Out-of-order data allowed delay (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 synchronization on 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-align by protocol: http→7400, https→7401) |
engine.cep.server.protocol | http | ${PP_CEP_PROTOCOL:http} | CEP protocol. Default http for internal communication, https for distributed/containers |
engine.cep.server.api.key | (None) | ${PP_CEP_API_KEY} | For CEPClient X-API-Key header. Must match CEP server 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 | — | Timeout message storage method (FILE_QUEUE/MEMORY) |
engine.streaming.messaging.timeout.recovery.type | DB | — | Timeout message recovery source (DB/FILE) |
Job Worker Thread
| 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 activation |
engine.dds.data.tag.enabled | true | — | Tag data DDS publishing |
engine.dds.data.asset.enabled | true | — | Asset data DDS publishing |
Anomaly Detection / Data Flow
| Property | Default | Substitution | Description |
|---|---|---|---|
engine.data.anomaly.enabled | false | — | Anomaly detection activation (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 | — | Data flow 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 (typo in class name Hanlder is as per 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 log. true for debugging sessions only |
flow.stats.prune.interval.ms | 86400000 | — | Counter cleanup execution cycle (ms, 24 hours). Aster TSDB counter tables cannot use default_time_to_live, so cleanup is at the application level |
flow.stats.prune.hourly.days | 30 | — | Retention period (days) of hour_bucket in tm_flow_stats_hourly |
flow.stats.prune.daily.days | 90 | — | Retention period (days) of day_bucket in tm_flow_stats_daily |
flow.health.check.interval.ms | 600000 | — | Flow health check cycle (ms, 10 minutes). ERROR if current time bucket errors > 0, 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 |
Aster TSDB tables used by 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 cycle (ms, 10 minutes) |
The determination source is the _ON / _NORMAL flags in the Aster TSDB tm_edge_status payload. There is no RT REST call, so the load is zero and it completes within 1 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 authentication 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 whitelist. Only when configured, if remoteAddr is on the list, the first IP of X-Forwarded-For is used as 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 | — | Expiry(expires_at < now)/Revocation(revoked_at IS NOT NULL) token cleanup |
engine.api.token.cleanup.retention_days | 30 | — | Hard delete from mm_token after this period expires. Once every 24 hours (first run 60s after boot) |
engine.api.token.default_ttl_days | 90 | — | Default expiry (days) when issuing new tokens. 0 = permanent token. If the issuance request provides ttl_days, that value takes precedence → Issue API Token |
Existing tokens where expires_at is NULL are maintained as permanently active (backward compatibility).
plantpulse-storage.properties
Path: plantpulse-server/config/plantpulse-storage.properties · Template: template/plantpulse-storage.properties
Manages connections and storage policies for Metastore (PostgreSQL), Cache (Valkey), Time Series (Aster TSDB), TSE, and Data Gateway.
⚠️ Typo keys actually exist in this file. Three keys have spellings different from what the code reads — you must 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 query string ?characterEncoding=UTF-8&reWriteBatchedInserts=true |
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. Internal clients default to redis:// |
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 identity verification |
The authoritative source for key constants is InMemoryConfigKeys in plantpulse-inmemory.
TSE (TimeSeries Engine)
| Property | Default | Substitution | Description |
|---|---|---|---|
tse.protocol | http | ${PP_TSE_PROTOCOL:http} | TSE protocol. Default plaintext http for internal communication (2026-08) |
tse.host | — | ${PP_TSE_HOST} | TSE host |
tse.port | 7800 | ${PP_TSE_CONNECT_PORT:7800} | TSE port. Default plaintext listener 7800 |
tse.user | tse | ${PP_TSE_USER:tse} | TSE account |
tse.password | tse123! | ${PP_TSE_PASSWORD:tse123!} | TSE password |
Aster TSDB Time Series Storage
| Property | Default | Substitution | Description |
|---|---|---|---|
storage.db_type | CASSANDRA | — | DB type. CASSANDRA/DSE/SCYLLADB remain in template comments, but the only currently supported value is CASSANDRA |
storage.db_version | 5.0 | — | Not the server version, but «feature compatibility level» — see note below |
storage.host | — | ${PP_CASSANDRA_HOST} | Contact point host |
storage.db_version is 5.0, but nodetool version appears as 6.0.0Both are correct. They are not values referring to the same thing.
| Value | What | |
|---|---|---|
storage.db_version | 5.0 | Compatibility level determining which feature set the driver speaks with |
| Actual Bundled Server | 6.0.0 | apache-cassandra-timeseries-6.0.0.jar — Internal fork of cassandra-6.0 |
Do not read 5.0 and assume «the server is 5.0». Information regarding the server is in Time Series Database.
Do not arbitrarily increase this value to 6.0 — driver behavior will change.
| storage.port | 9042 | ${PP_STORAGE_PORT:9042} | Native protocol port |
| storage.keyspace | — | ${PP_KEYSPACE} | Keyspace used |
| storage.user | — | ${PP_CASSANDRA_USER} | Connection account |
| storage.password | cassandra | ${PP_STORAGE_PASSWORD:cassandra} | Password. PP_STORAGE_PASSWORD is not exported in env.sh → default applied |
| storage.durable_writes | true | — | Use commit log (⚠️ No reading code — 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 Cycle
| Property | Default | Description |
|---|---|---|
storage.asset.data.snapshot.interval | 10 SECONDS | Records the tag map (data_map) of assets before each cycle to the asset_data series (data/second/timestamp + min/hour/day boundaries) |
Template comments explain the reasoning: 1 SECONDS was congested with approx 22K writes per minute based on 121 assets (approx 4% of total Aster TSDB). Since tag originals (second unit) are fully preserved in tm_tag_point/TSE, asset view snapshots were lowered to 10s (matching code default) — reducing writes by 90% while maintaining record integrity.
This value becomes the
ASSET_DATA_JOBcycle of the scheduler. It must be in"<n> <unit>"2-token format; refer to the scheduler architecture for rules.
Internal Metastore Synchronization (Experimental)
| Property | Default | Description |
|---|---|---|
storage.internal.metastore.enabled | false | Internal metastore synchronization activation |
storage.internal.metastore.thread.count | 4 | Number of synchronization worker threads |
storage.internal.metastore.queue.size | 12000000 | Synchronization queue size |
storage.internal.metastore.point.retation.days | 10 | Point retention period (⚠️ Typo key — see below) |
Row Cache
| Property | Default | Description |
|---|---|---|
storage.asset.data.row.cache.size | NONE | Asset data row cache (NONE/ROWS_ONLY/ALL) — ⚠️ No reading code |
storage.tag.point.row.cache.size | NONE | Tag point row cache — ⚠️ No reading code |
Compaction
| Property | Default | Description |
|---|---|---|
storage.table.compaction.strategy | UCS | Default strategy (TWCS: TimeWindow, UCS: Unified). Read by CassandraCreateDAO (Code fallback: TWCS) |
storage.table.compaction.strategy.twcs.window_size | 1 | TWCS window size — ⚠️ No reading code |
storage.table.compaction.strategy.twcs.window_unit | DAYS | TWCS window unit — ⚠️ No reading code |
storage.table.compaction.strategy.ucs.scailing_parameter | T8 | UCS scaling parameter (T8 = tiered 8 levels). Key spelling scailing matches 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 number of 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 key, see below |
storage.tag.point.select.limit.size | 1000000 | — | Max rows for single tag point select |
storage.doamin.model.changed.to.metastore.backup | true | — | Metastore backup upon domain model change — ⚠️ Typo key + No reading code |
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 Key — Ignored even if edited
The following 3 keys actually existing in the template have spellings different from what the code reads. Even if an administrator changes the values in these lines, they will be silently ignored, and the code defaults will be applied.
| Key in Template (Typo) | Key actually read by code | 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 this even with correct spelling) | — | — |
To actually change the value, add the key with the correct code spelling to the same file. For example, to increase the zstd level to 6, you must add
storage.compression.zstd.level=6(do not just fix the typo key, but add a new line with the correct spelling). Leaving the typo line is harmless, but fixing the template itself is the fundamental solution.No code reads
storage.doamin.model.changed.to.metastore.backupregardless of spelling. Backup during domain model change is handled byMetastoreBackupDeployer(cron0 0 12 1 1/1 ? *, 1st of every month 12:00), not this key — refer to the scheduler architecture.
Storage Keys Read by Code but Missing from Template
| Property | Code Default | Description |
|---|---|---|
storage.compression.zstd.type | ZstdCompressor | Compressor class name |
Storage Template Keys with No Reading Code (Invalid if edited)
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 keys above)
Even if the two keys related to TWCS are changed to storage.table.compaction.strategy=TWCS, they cannot 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 publishing topics. The code-side SSOTs are MqConfig (connections) 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 | — | MQTT TLS use |
In addition to 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 in template, defaults to mq.ssl.* 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 complete list of keys and defaults is organized in Messaging Architecture → DDS Publishing Topics. Topic values are Kafka/MQTT wire contracts, and UNSTopicDefaultsContractTest freezes the literals, so arbitrary changes are prohibited.
${PP_TOPIC_PREFIX} simultaneously determines the prefix of Kafka topics (pp-), the root of MQTT UNS paths (pp/), and the SparkplugB group ID.
Mail Configuration (pp.mm_config)
Authoritative source: PostgreSQL pp.mm_config · Key authoritative source: plantpulse.core.config.MailConfigKeys.
Reading side: SMTPMailSender.loadMailConfig().
PP_MAIL_* environment variables also do nothingSame as AI — the substitution layers for plantpulse-mail.properties and ${PP_MAIL_*} were removed on 2026-09-17~18. Providing them via sidecar or docker run -e will be silently ignored. Enter values in the console Configuration Screen(/config/index) or the pp.mm_config table.
| Config Key | Default | Description |
|---|---|---|
MAIL_SEND_ENABLED | false | Master Sending Gate. If off, skip without SMTP connection and leave only WARN |
MAIL_SMTP_HOST | smtp-relay.brevo.com | SMTP relay address |
MAIL_SMTP_PORT | 587 | 587 = STARTTLS, 465 = Implicit TLS (with MAIL_SMTP_SSL_ENABLE) |
MAIL_SMTP_AUTH | true | Whether to use SMTP login. Even if on, if MAIL_SMTP_USER is empty, no login is performed |
MAIL_SMTP_USER | (Empty) | SMTP account. May differ from sender address as it is the name issued by the relay. Can be saved as empty |
🔑 MAIL_SMTP_PASSWORD | (Empty) | SMTP password or API key. Authentication will be rejected if empty on a login-requiring relay |
MAIL_SMTP_STARTTLS_ENABLE | true | Whether to upgrade to STARTTLS on 587 |
MAIL_SMTP_SSL_ENABLE | false | Whether to connect via TLS from the start like 465. In this case, starttls is disabled |
MAIL_SMTP_CONNECTIONTIMEOUT | 10000 | Connection timeout (ms). If absent, JavaMail default is «infinite wait» |
MAIL_SMTP_TIMEOUT | 10000 | Read timeout (ms). 〃 |
MAIL_SMTP_WRITETIMEOUT | 10000 | Write timeout (ms). 〃 |
MAIL_FROM | (Empty) | Sender address. If empty, MAIL_SMTP_USER is used, but relay account names are often not valid addresses and may be rejected by the recipient — use a real address |
sms.sender.class | plantpulse.core.service.support.alarm.sms.DefaultSMSSender | SMS sender implementation FQN. If a non-existent class is entered, it reverts to default implementation |
TLS only uses 1.3 · 1.2 and verifies server certificate names.
The defaults for MAIL_SMTP_USER · MAIL_SMTP_PASSWORD are empty values. Since MAIL_SEND_ENABLED is the default false, sending will not be attempted until an operator enables it.
Sending via Gmail SMTP Relay
Google Workspace SMTP relay (smtp-relay.gmail.com) can be authorized by the sending server's IP address. In this case, since there is no account, leave login disabled. First, register the outgoing IP of this server in the Google Admin Console's SMTP relay settings.
| Config Key | Value |
|---|---|
MAIL_SEND_ENABLED | true |
MAIL_SMTP_HOST | smtp-relay.gmail.com |
MAIL_SMTP_PORT | 587 |
MAIL_SMTP_AUTH | false |
MAIL_SMTP_USER | (Empty) |
MAIL_SMTP_STARTTLS_ENABLE | true |
MAIL_SMTP_SSL_ENABLE | false |
MAIL_FROM | Actual address of Workspace domain |
After saving, click Re-apply on the configuration screen.
Alarm Mail
- The product logo is included in the mail header (CID inline image). It is visible even without a console address and is not fetched externally.
- The location line of the mail is written starting from the site (Site › Area › Line › Equipment; company is excluded).
- The «Go to check alarm» link is
PP_UI_CONSOLE_URL(<콘솔 주소>/alarm/view/<알람 번호>). If empty, the mail is sent as is, but only the link is omitted — Address Configuration.
diagnostic.log.email is goneIt was removed from the table as there is no code that reads it (product testing prevents that key from returning). Even if it remains somewhere, it does nothing.
AI Configuration (pp.mm_config)
Authoritative source: PostgreSQL pp.mm_config · Key authoritative source: plantpulse.core.config.AiConfigKeys.
Reading side: AiConfig (+ AiConfig.mcpSettings()).
PP_AI_* environment variables do nothingOld plantpulse-ai.properties was deleted on 2026-09-17, and the path that read that file and the ${PP_AI_*} substitution layer were both removed on 2026-09-18. Providing names like PP_AI_OPENAI_ENABLED via sidecar or docker run -e will be silently ignored — no errors or warnings.
This is why the product removed those names instead of keeping them «just in case». Names that exist but are ignored are worse than non-existent names because the server appears to be configured.
There are only two ways to enter values — the configuration screen in the console (/env), or directly modifying the pp.mm_config table. Keys without a row are read as the defaults in the table below; a row is created when a value is entered.
LLM Gateway
| Property | Default | Description |
|---|---|---|
ai.llm.enabled | false | Global AI switch. If off, all AI screens and endpoints are closed |
ai.llm.base.url | https://api.openai.com | OpenAI compatible gateway (LiteLLM/vLLM). Do not append / to the end |
ai.llm.chat.path | /v1/chat/completions | Change only when the gateway uses a different path |
ai.llm.responses.path | /v1/responses | 〃 |
🔑 ai.llm.api.key | (Empty) | Gateway key. Calls will be rejected with 401 if empty |
ai.llm.model | gpt-5-mini | Model name exposed by the gateway. If base.url is changed, change this as well |
ai.llm.timeout.ms | 60000 | LLM call timeout (ms) |
ai.llm.max.output.tokens | 4000 | Maximum response tokens |
ai.llm.temperature | -1 | If negative, omit entirely from the request to use the model's default |
ai.openai.* is no longer readThe prefix has changed to ai.llm.. Even if a ai.openai.base.url row remains in pp.mm_config, the code doesn't read it and uses the default (https://api.openai.com) — this creates a situation where it looks like the gateway is configured, but it attempts to go to OpenAI and fails. Check these rows on upgraded installations.
ai.llm.temperature negative by defaultThe gpt-5 series does not support temperature and returns 400 Unsupported value if a value other than the default 1 is sent. Therefore, a negative value meaning «do not send» is used as the default. Increase it to 0 or above only when using other models behind LiteLLM/vLLM.
Feature Switches
🔴 Defaults changed on 2026-09-16. The only AI feature remaining on the screen is Chat; others were removed by the product. Returning values to true will bring back the features, but those screens are not in a validated state for this version.
| Property | Default | Description |
|---|---|---|
ai.chat.enabled | true | AI Chat Console — This is the only AI feature remaining on the screen |
ai.chat.history.ttl.days | 90 | Chat history retention (days). Applied as Cassandra TTL and reflected from «new» messages after restart |
ai.alarm.rca.enabled | false | Alarm RCA — if on, AI root cause analysis is added to alarm details |
ai.alarm.eql.enabled | false | Alarm EQL Assistant — if on, a draft button is added to the alarm configuration form |
ai.uns.topic.enabled | false | UNS Topic Description — if on, a description panel is added to the UNS tree |
ai.result.audit.enabled | false | AI Result Audit — Internal RCA component without a screen. Enable this when enabling RCA |
ai.similar.incident.enabled | false | Similar Incident Evidence Collection — 〃 |
ai.similar.incident.max.items | 5 | Maximum number of similar incidents to include as evidence |
ai.similar.incident.min.score | 40 | Minimum score to be recognized as a similar incident |
3 keys for ai.diagnostic.summary.* (AI summary of diagnosis logs) and 3 keys for ai.operations.report.* (AI operation report) were removed from the product on 2026-08-25. Screens, JS, and controllers were also removed, so the features will not return even if values are restored. Leaving them causes no harm — no code reads them.
MCP
🔴 MCP is «independent» from ai.llm.enabled. Since it is for external clients, not screens, /api/v5/mcp remains open even if the gateway is off. For details, see MCP Server.
| Property | Default | Description |
|---|---|---|
AI_MCP_ENABLED | true | If false, then /api/v5/mcp returns 404 |
AI_MCP_ALLOWED_ORIGINS | (Empty) | Browser Origin whitelist (comma separated). If empty, all browser requests are rejected, allowing only native clients |
AI_MCP_TREE_MAX_NODES | 2000 | Tree response node upper limit — dependent on model context window; increase as model size increases |
AI_MCP_TREE_DEFAULT_DEPTH | 2 | Tree query default depth |
AI_MCP_TREE_MAX_NODE_TAGS | 100 | Tag count upper limit per node |
AI_MCP_TREE_ONTOLOGY_MAX_CHARS | 200000 | Ontology response character upper limit |
AI_MCP_TREE_ONTOLOGY_DEFAULT_DEPTH | 3 | One level deeper than default tree depth — must reach equipment nodes with tag counts to answer «how many tags for this equipment» as a default response |
AI_MCP_TREE_ONTOLOGY_MAX_TAGS | 500 | Tag row budget for the entire ontology response (separate from per-node limit). Since one slim tag is approx 85B, loading the entire site consumes 33~56% of the context window |
Other Templates
template/ contains application and infrastructure configuration templates in addition to the 5 types above. The authoritative 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/ | JDBC Integrated Configuration |
plantpulse-data-gateway.properties | /plantpulse-data-gateway/config/ | Data Gateway Operation |
plantpulse-warehouse.properties | /plantpulse-warehouse/config/ | Aster TSDB → Iceberg(S3) Archive |
plantpulse-datalake-admin-api.properties | /plantpulse-datalake-admin-api/config/ | Admin Console API · 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/Analysis 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/... | Analysis |
workflow.yaml · application.yaml | /plantpulse-workflow/... | Temporal / Kestra |
plantpulse-websocket.propertiesalso does not exist. There are no WebSocket/STOMP series configuration files.template/cluster/is a worker-node-specific override — if the master template is modified, this must also be synchronized.
plantpulse-batch.properties
Consists of connection blocks (Platform/Metastore/Cache/TSE/Kafka/Aster TSDB/Spark) and batch operation settings. Note that connection keys differ from storage/jdbc — e.g., Aster TSDB uses 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 input topic |
kafka.group.id | PP-BATCH-GROUP | Batch consumer group |
batch.timer_schedule_delay | 5000 | Batch timer cycle (ms) |
batch.thread_pool_batch_thread_size | 4 | Number of batch worker threads |
batch.buffer_drain_limit_size | 10000 | Max items to empty at once |
batch.sync.inmemory.enabled | false | In-memory synchronization |
batch.sync.inmemory.ttl.minute | 10 | In-memory synchronization TTL (minutes) |
batch.sync.tse.enabled | true | Distribution to TSE |
batch.sync.external.db.enabled | false | External DB synchronization |
external.db.name | TIMESCALEDB | External DB type. INFLUXDB/QUESTDB blocks are in comment state in the template |
analytics.url | jdbc:spark://${PP_SPARK_HOST}:${PP_KYUUBI_PORT}/default | Spark Thrift JDBC |
plantpulse-cep.properties
| Property | Default | Description |
|---|---|---|
cep.backend | aster | Backend to boot (aster · flink). Any other value causes boot failure — Backends |
cep.aster.state.enabled | true | Preserve Aster CEP rule states (window · aggregation · in-progress pattern) in cache.* Redis. Key is PP:CEP:ASTER-STATE:<문장> |
cep.aster.state.snapshot_interval_ms | 60000 | Snapshot cycle — maximum state that can be lost during crash. Normal shutdown takes a final snapshot before exiting |
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 thread |
cep.stream.shard_count | 16 | Stream shard count |
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 cycle |
cep.monitor.store_interval_seconds | 60 | Aster TSDB metric storage cycle |
cep.api.key | ${PP_CEP_API_KEY} | EQL management endpoint authentication (X-API-Key) |
cep.api.max_eql_text_length | 100000 | Max EQL text length (DoS defense) |
cep.storage.request_timeout_seconds | 10 | Aster TSDB request timeout |
cep.storage.connect_max_retries | 3 | Connection retry |
cep.storage.ttl_seconds | 864000 | Metric 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 cycle |
cep.stream.max_clean_duration_ms | 30000 | Max clean duration |
cep.stream.cleaner_max_claim_count | 5000 | Max claim count |
cep.stream.ack_chunk_size | 500 | ACK chunk size |
cep.stream.max_deliveries | 3 | Events are sent to dead letter only after being delivered this many times without being processed |
cep.stream.dead_letter.retention_ms | 86400000 | Dead letter retention age (24 hours) |
cep.stream.dead_letter.max_bytes | 268435456 | Dead letter byte budget (256 MiB, total across shards). If exceeded, oldest entries are cut from each shard |
plantpulse-jdbc.properties
Integrated connection settings for external JDBC clients (SQL module · Data Gateway). Prefix is 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 configuration consistency (not used functionally) |
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 Analysis |
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 caught as tss/7801, JDBC connection fails → data-gateway boot fails → all/api/v1/*return 404 → analytics catalog 404. Therefore, it is pinned tots/PP_TSE_PORT.
jdbc.spark.usernameis the analysis-only account${PP_ANALYTICS_USER}. It is separate from the Kyuubi internal metastore account (PP_HIVE_*). Optional keysjdbc.<tech>.pool.initial|min|max(postgres/spark/tse) andjdbc.<tech>.query.timeout|fetch-sizecan be used (full reference inplantpulse-jdbc/docs/configuration.md).jdbc.cassandra.local-datacenteris in a comment state in the template and isdatacenter1when unconfigured.
plantpulse-data-gateway.properties
| Property | Default | Description |
|---|---|---|
data.gateway.api.key | ${PP_DATA_GATEWAY_API_KEY:ADFA-URDV-QWED-1234} | API authentication key (Required). Dev default to be replaced upon 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-datalake-admin-api.properties
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 (.) for keys.
| Property | Default | Description |
|---|---|---|
type | MASTER | Single node or cluster master |
db_type | CASSANDRA | Monitored DB type |
master / host | ${PP_MASTER_IP} / ${PP_HOST_IP} | Master / Self host |
db_host · db_port · db_keyspace · db_username · db_password · db_replication_factor | ${PP_CASSANDRA_*} · 9042 · ${PP_SCHEME} · 1 | Aster TSDB connection |
tse_host · tse_port · tse_protocol · tse_username · tse_password | ${PP_TSE_*} · 7801 · https · tse · tse123! | TSE connection (Metric loading) |
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 | Aster TSDB 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, Aster TSDB 7199, Spark 10010.
plantpulse-warehouse.properties
Aster TSDB → Iceberg(S3) archive batch. Only this file uses UPPER_SNAKE_CASE keys (UPPER_SNAKE).
| Group | Key | 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-ARCHIVER-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) |
| Script | 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_S3_BUCKET:-plantpulse}/spark-checkpoint/ |
| Source/Target | CASSANDRA_CATALOG · CASSANDRA_DATABASE · S3_BUCKET · ICEBERG_CATALOG · ICEBERG_DATABASE | cassandra · ${PP_SCHEME:pp} · ${PP_S3_BUCKET:-plantpulse} · spark_catalog · ${PP_SCHEME:pp} |
| Optimization | SNAPSHOT_RETENTION_DAYS · OPTIMIZER_MIN_INPUT_FILES · OPTIMIZER_TARGET_FILE_SIZE_BYTES | 3 · 10 · 134217728 (128MB) |
| Timeout | 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 Template
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 — Auth source pointed to by HiveMQ security extension (FileAuthAuthenticator) via HIVEMQ_AUTH_FILE environment variable. Only two keys: mqtt.user / mqtt.password.
hive-auth.properties — Read by plantpulse-hive-auth (PasswordAuthenticator). Either HIVE_AUTH_FILE environment variable or $PP_HOME/plantpulse-analytics/spark/conf/hive-auth.properties. Notably, the key name itself is a placeholder.
${PP_HIVE_USER}=${PP_HIVE_PASSWORD}
${PP_ANALYTICS_USER}=${PP_ANALYTICS_PASSWORD}
kafka.properties — KRaft mode Kafka broker. Main values: process.roles=broker,controller, node.id=1, num.partitions=4, 3 types of listeners (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. Based on the premise that Kafka acts as a buffer and persistence is handled by Aster TSDB/TSE. If a consumer stops for more than 1 hour, data is lost.
Plugin Template
plantpulse-plugin-opcua.properties — PostgreSQL / Aster TSDB / Valkey / Kafka connections + OPC UA server endpoint.
| Property | Default | Description |
|---|---|---|
kafka.topic | pp-tag-point | ⚠️ Literal — not ${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 settings.
| 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 reception |
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 registry/AAS ports are hardcoded in code, not configured —
REGISTRY_PORT=4800,AAS_PORT=4801ofAASServer.java.
Engine Keys Read by Code but Missing from Template
EngineConfig also reads the following keys. Since they are not in the template (plantpulse-engine.properties), code defaults apply normally. To change a value, you must directly add a line 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 scheduler job cycles — refer to 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. Since the template specifies them astrue, they are enabled only in the deployed version — deleting these three lines will silently disable DDS.
Job Thread (Not listed in 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 is8(engine.job.thread.assetmatches as8on both sides). Deleting the template line will halve the value.
Flow Engine (Not listed in 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 Metric / 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 |
Non-existent Settings
The following names appear in old documents or external materials but do not exist in templates or code. Adding settings with these names will have no effect.
| Item 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. | storage.tag.point.ttl / storage.asset.data.ttl etc. per table (TTL section) |
storage.compression.enable / .algorithm | storage.compression.zstd.level / storage.compression.zstd.type (Template lines are typos — Typo Key Warning) |
storage.compaction.enable / .schedule | storage.table.compaction.strategy series (Compaction section) |
storage.metastore.* / storage.cache.* | None |
mq.type / mq.kafka.bootstrap.servers / mq.kafka.group.id | mq.host + mq.kafka.port. Consumer groups are code constants (PP-KAFKA-POINT-GROUP etc., Messaging Architecture), not configuration |
mail.* in file | Only one place: pp.mm_config (Migrated 2026-09-17, file deleted) |
websocket.* / plantpulse-websocket.properties | None. STOMP/WebSocket retired 2026-07, browser push is SSE (/push/sse) — Messaging Architecture |
scheduler.enabled / scheduler.thread.count / scheduler.misfire.* / scheduler.history.* | None. Quartz configuration is 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, Topic prefix is 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 in env.sh |
Properties Containing Passwords
Values that must be replaced before delivery. Dev plaintext defaults are placeholders in env.sh (or template); for production/customer delivery, replace with customer-specific values or overwrite via Docker -e VAR=... / export.
| File | Key |
|---|---|
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 |
pp.mm_config (Mail) | mail.smtp.password |
pp.mm_config (AI) | ai.llm.api.key — No fallback for old name ai.openai.* |
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-datalake-admin-api.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 | Entire value is password |
API Key Policy:
PP_API_KEY·PP_DATA_GATEWAY_API_KEY·PP_FLOW_WEBHOOK_API_KEY·PP_CEP_API_KEYuse a dev plaintext default fallback (${VAR:-<dev-default>}) — this allows dev/test to boot and deploy without additional secret injection. Do not change to hard-fail (:?) — ifrestart-server.shsourcesenv.sh, it will crash immediately and deployment will fail.
Recommended Action
Secrets are not written in configuration files. Collect them in a single sidecar file and use 0600.
ls -l /etc/kopens/plantpulse-platform.env # 비밀 — 0600, repo 밖
ls -l /etc/kopens/platform.node.env # 이 박스 고유 값 — 0600
# 값을 바꿀 때는 파일을 열지 말고 도구를 씁니다
cd /opt/kopens/plantpulse-platform-docker
bin/passwd.sh --list # 바꿀 수 있는 키
bin/passwd.sh PP_PG_PASSWORD # 서버 계정 변경 → 사이드카 → 재렌더 → 재시작
Since the rendered configuration files contain the substituted values, do not commit those files to the repository.