Gateway Configuration
Development Preview
This page is based on the runtime parsers at commit b1a16cbc (GatewayConfig, TagConfig::fromJson, etc.) and the active callbacks. It also includes keys from the project configuration documents that the runtime does not read yet, clearly marked as such. Writing configuration does not mean drivers are registered or end-to-end functionality is verified.
1. Configuration File and How It Takes Effect
The main configuration file defaults to config/gateway.json, and the process specifies its path with --config. If the configuration file does not exist, it is created automatically with built-in defaults; on load, the file contents override the defaults (merge); on save, a temporary file is written first and then atomically renamed, avoiding corruption from power loss.
The following keys are read directly by the current code:
| Key | Current purpose |
|---|---|
web.port | HTTP port for the Web UI/API, default 8080 |
system.logLevel | Log level, such as trace, debug, info, warn, error |
mqtt.broker / mqtt.port | Broker host and port; the host name carries no tcp:// or ssl:// prefix |
mqtt.clientId | MQTT Client ID |
mqtt.username / mqtt.password | Broker authentication credentials |
mqtt.productId / mqtt.deviceNum | Product/device segments of the property-reporting and function topics |
mqtt.keepAliveSec | MQTT Keep Alive seconds, default 60 |
mqtt.connectTimeoutMs | Connection timeout (milliseconds), default 10000 |
mqtt.cleanSession | MQTT Clean Session, default true |
mqtt.qos | Publish and subscribe QoS, default 1 |
mqtt.tls | Whether to use an ssl:// connection; the certificate fields must be configured correctly and separately |
mqtt.caCert / mqtt.clientCert / mqtt.clientKey | Optional TLS CA, client certificate, and private key paths |
mqtt.autoReconnect / mqtt.reconnectIntervalMs / mqtt.maxReconnectAttempts | Paho client auto-reconnect options and base interval; not a production disconnected-data guarantee |
drivers | Top-level driver configuration array, see section 4 |
engine.defaultPollMs / engine.maxTags / engine.batchSize | TagEngine default polling interval, Tag limit, and batch size |
alertManager.maxHistoryDays / alertManager.maxActiveAlerts / alertManager.deduplicateEnabled | Alarm history retention days (30), maximum active alarms (1000), and alarm deduplication |
ota.versionApiUrl / ota.binaryPath | OTA version check URL and executable file path |
certificates.certDir / certificates.enabled | TLS certificate directory and switch |
PUT /api/mqtt/config currently only handles broker, port, clientId, username, and password, then saves the configuration; it does not reconnect automatically as the Web wording suggests. A complete MQTT change should edit the real configuration and restart the process, after first resolving the read-only mount issue in deployment.
2. Default Configuration Keys Not Yet Read by the Runtime
The repository's default config/gateway.json also contains the following keys. They come from the design goals in the project configuration documents, but the current GatewayConfig runtime code has no reader for them — do not treat these keys as effective configuration:
gateway.name/gateway.webPort/gateway.logLevel/gateway.dataDir: legacy key names; the actively read keys areweb.portandsystem.logLevel.network.*(wifiMode/wifiSsid/apSsid, etc.): design configuration for WiFi/AP provisioning; currently the responsibility of the NetworkManager module, and how it takes effect depends on that module's implementation.cache.*(enabled/maxRecords/maxSizeMB/dbPath): design configuration for offline caching;SqliteCacheis constructed but not wired into the active reporting path.engine.defaultPollIntervalMs/engine.reportIntervalMs/engine.batchMergeEnabled: engine extension design keys; the actively read keys areengine.defaultPollMsand the others above.mqtt.reconnectMinSec/mqtt.reconnectMaxSec: exponential backoff design keys; the actively read key ismqtt.reconnectIntervalMs.- Environment-variable overrides such as
FASTBEE_MQTT_*,FASTBEE_WEB_PORT, andFASTBEE_LOG_LEVEL, as well as configuration hot reload, are design goals with no complete implementation at present;TZin Compose should not be extrapolated into a general configuration override mechanism.
3. Configuration Encryption and Secrets Management
At startup, AES encryption support is enabled for the configuration (ConfigCrypto): sensitive fields in the configuration file are stored as ENC(...) ciphertext, decrypted on load and encrypted on save. The key file is .secret.key in the configuration directory (created automatically on first startup).
- When backing up ciphertext configuration, the key must be protected in sync according to the security policy, and it must never be committed to a public repository.
ENC(...)ciphertext depends on the matching key and cannot be ported across environments; replace it with the credentials of the local environment when deploying.- Compose's read-only
configmount blocks key creation and API saves; this must be explicitly resolved in the deployment design. - The default
admin/admin123must be changed immediately; anonymous Mosquitto is only acceptable in an isolated development environment.
4. Driver Configuration (the drivers Array)
The runtime reads the following fields from the top-level drivers array (compatible legacy keys are noted):
| Field | Description |
|---|---|
protocolName | Protocol name; modbus in the Modbus example, default modbus |
connectionType | tcp or rtu, default tcp |
host / port | TCP target address and port, default 127.0.0.1:502 |
serialDevice | RTU serial device path; the legacy key serialPort is also accepted |
baudRate | Serial baud rate, default 9600; the legacy key baudrate is also accepted |
parity | Integer parity: 0=N, 1=O, 2=E |
dataBits / stopBits | Serial data bits (8) and stop bits (1); lowercase legacy keys are also accepted |
timeoutMs / retryCount | Request timeout (3000ms) and retry count (3) |
pollIntervalMs | Driver polling interval (1000ms) |
TCP example:
{
"drivers": [
{
"protocolName": "modbus",
"connectionType": "tcp",
"host": "192.0.2.10",
"port": 502,
"timeoutMs": 3000,
"retryCount": 3,
"pollIntervalMs": 1000
}
]
}192.0.2.10 is a documentation-only reserved address. Real libmodbus driver code exists, but the current application only loads and logs these configurations; it does not create, initialize, or register drivers, and other protocols cannot bypass this shared blocker either. OPC UA is optional/under development only; BACnet, IEC104, S7, and DLT645 should currently all be treated as Stub/integration-in-progress boundaries — see sections 7 and 8 of the overview.
5. Device and Tag Fields
5.1 Sub-devices
POST /api/devices and PUT /api/devices/:id currently parse:
deviceId: internal unique ID;dev-<timestamp>is generated when it is empty on creation.productId,deviceNum,deviceName: product, device number, and display name.protocolType: protocol type, such asmodbus.online,lastHeartbeat: serializable status fields; they must not be treated as a physical online guarantee.
The id, protocol, slaveAddress, and similar fields the Web currently sends/displays are inconsistent with the runtime; do not infer the API model from the interface.
5.2 Tag Fields
POST /api/devices/:id/tags uses TagConfig::fromJson(). The fields actually parsed:
| Field | Description |
|---|---|
id, name | Tag identifier and display name; deviceId is overridden by the device ID in the route |
sourceType | Numeric source type: 0=MODBUS, 1=OPCUA, 2=BACNET, 3=IEC104, 4=DLT645, 5=S7, plus 10/11/12=GPIO_DI/DO/AI and 20=SYSTEM |
slaveAddr, registerAddr, registerCount | Modbus slave address, zero-based register address, and register count |
scale, offset, unit | Linear scaling factor, offset, and engineering unit |
scaleType, sqrtFactor | Scaling type: 0=NONE, 1=LINEAR, 2=SQRT, 3=CUSTOM; the coefficient for SQRT mode |
pollIntervalMs, deadband, reportOnChange, enabled | Sampling period (ms), deadband value, report-on-change, and enable switch |
nodeId | OPC UA node ID (e.g. ns=2;s=Temperature) |
objectType, instanceId, propertyId | BACnet object type, instance number, and property ID |
gpioChannel | GPIO channel number |
Notes:
registerAddris a zero-based address passed directly to libmodbus; use0for the first holding register rather than writing40001directly.- The current request parser does not read the
dataType,function/functionCode,byteOrder, orpollIntervalfields (byteOrderis always the default big-endian), even though responses may serializedataType; configuration acceptance must check the fields actually read. - Scaling conversion takes effect at the driver layer: for
LINEAR,engineering value = raw value × scale + offset; forSQRT,engineering value = √(raw value) × sqrtFactor; reverse conversion (control downlink) is only supported for LINEAR.
6. GPIO Configuration (gpio.json)
GPIO channel configuration is independent of the main configuration and is read by GpioManager; field details depend on its runtime implementation. The example structure given in the project documents:
{
"di": [{"channel": 0, "name": "door_sensor", "productId": "P003", "propertyId": "door"}],
"do": [{"channel": 0, "name": "relay_1", "productId": "P003", "propertyId": "relay"}],
"ai": [{"channel": 0, "name": "temp_sensor", "range": [4, 20], "unit": "mA", "engMin": 0, "engMax": 100}],
"led": [{"name": "power", "gpio": 17}, {"name": "network", "gpio": 27}, {"name": "fault", "gpio": 22}]
}Real GPIO requires compiling with -DFASTBEE_ENABLE_GPIO=ON and installing libgpiod; otherwise a simulated backend is used. For the stronger pin-based peripheral model (DI/DO/AI/AO/LED/buzzer/watchdog), see Peripherals and Linkage.
7. MQTT Topics and Active Data Formats
Property reporting topic:
/{productId}/{deviceNum}/property/postFunction subscription primitive topic (not currently called by production code):
/{productId}/{deviceNum}/function/#The JSON structure used by the active reporting callback:
{
"deviceId": "modbus-dev-001",
"timestamp": 1700000000000,
"tags": [
{ "id": "holding-0", "value": 25.5, "quality": 0 }
]
}quality values: 0=GOOD, 1=BAD, 2=UNCERTAIN, 3=TIMEOUT, 4=NOT_CONNECTED, 5=CONFIG_ERROR.
There is also a TagMapper::buildPropertyPost() that is not wired into the active callback and produces a different params format. At present, distinguish "mappers that exist in the code" from "the actual active reporting format". When MQTT is disconnected, the active callback skips the batch; constructing SqliteCache and setting the cache configuration does not mean offline replay is working, and production code does not call the function subscription either.
8. Alarm Rules and Aggregation Rules
Alarm rules are configured dynamically through the REST API (POST /api/alarms/rules) without modifying the configuration file. Rule JSON structure:
{
"id": "high-temp",
"name": "high-temp alarm",
"tagId": "temp1",
"type": "LEVEL",
"enabled": true,
"severity": "HIGH",
"hEnabled": true,
"highLimit": 80.0,
"hhEnabled": true,
"highHighLimit": 95.0,
"lEnabled": true,
"lowLimit": 10.0,
"deadband": 2.0
}| Field | Description |
|---|---|
type | Alarm type: LEVEL/DEVIATION/RATE_OF_CHANGE/QUALITY/DIGITAL |
severity | Severity level: LOW/MEDIUM/HIGH/CRITICAL |
highLimit / lowLimit | High/low thresholds (LEVEL type, supporting the four levels HH/H/L/LL) |
deadband | Deadband value, preventing jitter near the threshold |
deviationBand / rateLimit | Allowed deviation range (DEVIATION) / rate-of-change upper limit (RATE_OF_CHANGE) |
The alarm engine is linked to the TagEngine through TagChangeObserver: rules are evaluated automatically when a Tag value updates, with the state machine NORMAL → ACTIVE → ACKED → RETURNED. For rule management, active alarms, acknowledgement/silencing, statistics, and import/export interfaces, see the alarm engine section of the API.
The Tag aggregator supports MAX/MIN/AVG/TOTAL/DELTA/RATE sliding-window aggregation. Currently the only active routes are query interfaces (GET /api/aggregator/rules, GET /api/aggregator/rules/:id/result); no route for creating rules is registered, so a configuration entry point for rules awaits a later release.
The driver health monitor's parameters (maxConsecutiveErrors=10, watchdogTimeoutMs=30000, autoRecoverMs=60000) are set at registration time in the code, and runtime status is queried through GET /api/drivers/health; since the driver registration blocker has not been lifted, health data must be interpreted against that premise.
9. Secrets and Configuration Security Checklist
.secret.keyand ciphertext configuration must be isolated from public documents, images, and logs, and stored according to the organization's secrets management policy.- A complete MQTT change should edit the real configuration file and restart the process, rather than relying on the "save and reconnect" wording of the PUT interface.
tls: trueselects an MQTT TLS client connection, but the bundled Mosquitto listener on8883does not automatically get TLS configuration; the two must not be conflated.- On recovery, check the configuration path, key match, file owner, and write permissions together; a read-only configuration mount blocks
.secret.keyand API configuration writes, and the problem must not be masked by repeated restarts.

