Configuration

Axiom Border reads a single file, configuration.yaml, from the directory given by the AXIOM_CONFIG_DIR environment variable (default: config). An example file, configuration_example.yaml, is supplied alongside it — copy it rather than editing it in place, so you always keep an untouched reference.

cp config/configuration_example.yaml config/configuration.yaml

You will rarely edit the file by hand: the console’s Configuration view and the REST API write this same file — see Changing the configuration below.

No hot reload — restarts are mandatory

Every field in this file requires a service restart to take effect. There are no exceptions. The file is read once, when the service starts. If you change a value and nothing happens, you have not restarted the service. Saving from the console on a managed deployment restarts the service for you; everywhere else the restart is yours to run:

sudo systemctl restart axiom-border
An invalid file stops the service

If configuration.yaml is missing or is not valid YAML, Axiom Border refuses to start rather than running with defaults. This is intentional — a monitoring probe silently running on the wrong configuration is worse than one that will not come up. Check the log if the service does not start.

Changing the configuration

Three paths write the same file. The web console is the recommended one: it edits the settings a running deployment actually tunes, validates them before writing, and on a managed deployment restarts the service for you. The REST API covers automation and driving a probe without shell access. Editing the file over a shell always works — it is just the least guarded of the three.

Open Configuration in the side menu. The view edits configuration.yaml itself, one group of settings per tab:

The Configuration view, on the Automatic scans tab, with the restart and credentials notices above the fields

Each field is labelled in plain language rather than by its YAML key — nmap: period for schedule, vulnScan: depth for level — and the reference further down this page maps them to the keys they write. The two notices at the top are permanent, not the result of saving: they are there to tell you before you edit that nothing applies until the service restarts, and that the file holds credentials which are preserved for you.

Tab What it edits
Automatic scans The securityProbes switches and intervals: enable and schedule for nmap, vulnScan and snmp; the vulnerability scan’s level, severity and OT switches; the interfaces and flushInterval of sniffing
OpenGate The whole opengate block — connection, collect and provision
MQTT The whole mqtt block — the embedded broker with its WebSocket and TLS listeners, the internal publisher and the operations client

Everything else — logger, login, influxdb, apiPort, pagination and the per-probe details not listed above, scan targets included — is changed through the API or the file.

Network status has a shortcut straight here: its Automatic scans action opens this view on the first tab, which is where you land when a scheduled scan looks stale or too frequent.

The forms are a window onto the file, not a copy of it. Saving rewrites the whole file but preserves everything the forms do not manage: comments, credentials, and every key outside the three tabs. Leaving a field blank removes its key from the file, returning that setting to its unset behaviour. Durations, ports and cron expressions are checked as you edit, and Save stays disabled while any field holds an invalid value — an invalid document never reaches the probe.

What happens after Save depends on the deployment:

  • On a managed deployment, the console asks the deployment manager (Keystone) to restart the service and waits until it reports healthy again — when the green confirmation appears, the change is already live. The console is unresponsive for the few seconds the restart takes.
  • On a plain systemd installation, the file is written and an amber banner stays on screen until you restart the service yourself: sudo systemctl restart axiom-border.
  • If the automatic restart fails, the write has still succeeded. Restart by hand — keystonectl restart axiom-border, or sudo systemctl restart axiom-border — and the saved configuration applies.

Changing apiPort or the login credential raises an explicit lockout warning: the file is written anyway, so before restarting make sure you can reach the new port or know the new password.

GET /config returns the current YAML and PUT /config replaces it. This exists so a probe can be configured for a customer environment without shell access to the host.

# 1. Authenticate
curl -X POST http://localhost:8083/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"<user>","password":"<your-password>"}'

# 2. Download the current file as a starting template (authenticated — it contains secrets)
curl -H "Authorization: Bearer <jwt-token>" \
  http://localhost:8083/config -o configuration.yaml

# 3. Edit it, then upload the COMPLETE file
curl -X PUT http://localhost:8083/config \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/x-yaml" \
  --data-binary @configuration.yaml

# 4. Apply
sudo systemctl restart axiom-border

The PUT response reports what happened:

{
  "restartRequired": true,
  "message": "configuration written; restart axiom-border to apply",
  "backup": "/opt/axiom-border/config/configuration.yaml.bak",
  "warnings": ["apiPort changes 8083 -> 9083 (may affect API access after restart)"]
}
Field Meaning
restartRequired Always true — configuration only takes effect at startup
message Confirmation text
backup Path of the previous file’s backup. Omitted when there was no previous file
warnings Present only when non-empty. Raised when apiPort or the login credentials change, because either can lock you out

Console and API end in the same validated write, with three guarantees:

  • Validation before writing. The document is validated exactly as at startup and checked for the six mandatory fields. Invalid YAML or a missing field returns HTTP 400 and nothing is written.
  • Atomic replacement. The file is written to a temporary file in the same directory and renamed.
  • Backup of the previous file, forced to 0600 because it contains secrets.
The write is a full replacement, and the backup is a single level

PUT /config expects the complete file, not a patch — fetch, edit, send back whole. The console handles this for you and sends the full document with your edits applied.

The backup is always the same filename, configuration.yaml.bak, and it is overwritten on every write. There is only one level of history. If you need more, copy it aside yourself before saving. To recover from a lockout, restore that file and restart.

Where the file lives

Deployment Path
Installed with install.sh (systemd) /opt/axiom-border/config/configuration.yaml
Managed deployment /var/lib/axiom-border/config/configuration.yaml — a stable path outside the per-version working directory

Overriding any field with an environment variable

Every key can be overridden from the environment by upper-casing it and replacing dots with underscores. A .env file in the working directory is also loaded.

Configuration key Environment variable
logger.logLevel LOGGER_LOGLEVEL
influxdb.token INFLUXDB_TOKEN
login.pass LOGIN_PASS

This is the recommended way to handle secrets: keep the tokens and passwords out of the YAML file entirely and inject them through the environment or your secret manager.

Required fields

Only six fields are validated as mandatory. If any is missing, the configuration is rejected:

login.user · login.pass · influxdb.url · influxdb.token · influxdb.org · apiPort

Everything else is optional and has a documented default.

logger

Backend logging, to console and to size-rotated files.

Field Type Default Description
directory string ./logs Destination directory for rotated log files
inFile bool true Write to file
inConsole bool true Echo to stdout
colorInConsole bool true ANSI colour codes on stdout. Disable when piping to a file or to journald
logLevel string DEBUG DEBUG | INFO | WARN | ERROR
processId string "" Process label added to every line; empty means no label
fileName string egprobe.log Base log filename; rotation appends suffixes
maxSize int (MB) 20 File size before rotation
maxBackups int 20 Number of rotated files kept
compress bool true gzip rotated backups
Tip

DEBUG is the shipped default and it is verbose enough to flood a journal on a busy probe. For production, INFO is the sane choice.

login

The single credential that protects the whole API except /auth/login.

Field Type Required Description
user string Yes API username
pass string Yes SHA256 hex digest of the password, not the password itself

Generate the digest before writing it:

echo -n "yourPassword" | sha256sum

At login, the probe hashes the submitted password and compares it against this value.

Change the shipped credential

The example file carries a placeholder digest so that a fresh installation can log in. It must be replaced before the probe is reachable by anything. Treat a deployment still carrying the example digest as unauthenticated.

influxdb

Connection to the metrics database (InfluxDB 2.x) that stores metrics, audit records, scan results and alarms.

Field Type Default Required Description
url string http://127.0.0.1:8086 Yes Metrics database endpoint
org string axiom Yes Organisation
token string Yes Token with read/write permission on the organisation. Prefer injecting via INFLUXDB_TOKEN
buckets []string see below No Buckets created at startup if they do not already exist

Default bucket set: network_bucket, ssh_bucket, metrics_bucket, usb_bucket, eg_alarms, audit_logs, networktraffic, availability, scanmetrics. The four per-channel event buckets are populated by the oda-lite collector, the rest by the probe itself — see Metrics ingestion.

The probe starts even when the metrics database is unreachable, and every feature that depends on it is inoperative until it recovers. This is deliberate for edge deployments where the database may be a separate node that boots later.

alerts and audit

Block Field Type Default Description
alerts offset duration 10s Tolerance window applied to the alarm evaluation query range
alerts bucket string eg_alarms Bucket where alarms are written and read
audit bucket string audit_logs Bucket for audit events: logins, configuration changes, executions

Top-level keys

Field Type Default Required Description
apiPort string 8083 Yes HTTP service port. Binds on 0.0.0.0
outputParquetPath string ./ No Destination directory for on-demand Parquet exports
dbPath string "" No Local state database file. Empty resolves to ./db.dat, relative to the working directory
gojascriptsDir string "" No Directory holding rule scripts. Empty resolves to resources/gojascripts
gojaTimeout duration 8s No, but set it Maximum execution time for a rule script per event
Two settings worth pinning down

Always set dbPath to an absolute path in production (for example /var/lib/axiom-border/db.dat). Deployments that use per-version working directories will otherwise create a fresh, empty database on every upgrade and appear to have lost all state.

Always set gojaTimeout explicitly. Without a time limit, rule scripts do not run at all.

pagination

Controls the two read modes over the metrics database. Every field has a default, so the block can be omitted entirely.

Field Type Default Description
maxRecentEvents int 1000 Number of most recent events kept per stream, which is also the cap of the /recent/* feed
rangePageSize int 100 Default page size for range queries
rangeMaxPageSize int 500 Maximum accepted pageSize
rangeMaterializeMaxRows int 40000 Row threshold. Below it, the whole range is cached, which allows jumping to any page and reporting an exact total. Above it, results are delivered sequentially, page after page
rangeCacheTTL duration 60s Lifetime of a cached range entry
rangeCacheMaxEntries int 8 Maximum ranges cached at the same time. Beyond it, the least recently used range is discarded
rangeExportMaxRows int 500000 Row cap for the streaming CSV export. A range exceeding it returns HTTP 400 asking you to narrow the window

trainerDetails

HTTP client settings for the AI container. Only relevant on Linux with a container engine available — see AI capabilities.

Field Type Default Description
serviceTlsCert string "" Client TLS certificate towards the AI container
serviceTlsKey string "" Matching private key
trainerContainerName string trainer Base container name. The effective name is <trainerContainerName>-<agent>

Three sub-blocks — metrics, healthCheck and predict — share the same four fields. The {port} literal in each URL is substituted at runtime with the port of the corresponding AI agent.

Sub-block url timeout retries timeBetweenRetries Purpose
metrics https://127.0.0.1:{port}/api/metrics 2s 1 2s Collect metrics from the AI container
healthCheck https://127.0.0.1:{port}/health 2s 20 5s Wait for container startup — 20 attempts at 5 s gives roughly 100 s of margin
predict https://127.0.0.1:{port}/api/predict 10s 1 2s Inference, invoked from rule scripts
Note

Use whole seconds for timeBetweenRetries; fractions of a second are not honoured.

securityProbes

Security probes run two ways: automatically on an interval (schedule), and manually from the API or UI. Results are stored in local state and in the metrics database — scanmetrics for aggregates, availability for up/down.

Probes ship disabled, and the first scan runs at startup

nmap, vulnScan and snmp all default to enabled: false. Enable them only after confirming their dependencies are present on the target host, because the first scan of an enabled probe runs as soon as the service starts, not after the schedule interval has elapsed. A probe enabled without its dependencies — the nmap binary on PATH, a readable templates directory, a valid capture interface — will log errors on every boot.

securityProbes.nmap

Host discovery, port scanning and optional fingerprinting. Requires the nmap binary on PATH.

Field Type Default Description
enabled bool false Enable the scheduled probe
schedule duration 2h Interval between sweeps. Zero or negative falls back to 2h
targets []string ["192.168.1.0/24"] Hosts and CIDR ranges to scan
portFilter string "" Port list such as "22,80,443". Empty means nmap’s top 1000
udp bool false Add a UDP scan. Slow
udpPorts string common UDP ports UDP ports probed when udp: true
timing string "" T1T5. T2 is cautious, T4 is reasonable on healthy networks
scanPorts bool true When false, ping scan only (-sn)
service bool false Service and version detection (-sV)
os bool false OS fingerprinting (-O). Implies a SYN scan, which needs raw sockets and therefore root
timeout duration 5m Abort if nmap does not finish

securityProbes.vulnScan

Template-based vulnerability scanning, including the embedded OT/ICS suite. The scan engine ships inside the product — there is no scanner binary to install.

Field Type Default Description
enabled bool false Enable the scheduled probe
schedule duration 2h Sweep interval. Also re-triggered by event, with debounce, when new hosts are recorded
severity string (CSV) info,low,medium,high,critical Severity filter, given as one comma-separated string
level string profundo Depth, translated to template tags — see below
templatesDir string "" Templates directory override for the scheduled probe
defaultTemplatesDir string ./vulnscan-templates Deployment-wide fallback
timeout duration 5m Scan cut-off. A request may override it
allowTemplateUpdates bool true Whether the probe may refresh templates from the network
allowIntrusive bool false Master lock for OT/ICS intrusive mode
enableOT bool false Add read-only OT templates to the scheduled sweep
userAgent string "" HTTP User-Agent for the web checks. Empty keeps a neutral, randomised browser value per request; set it only when a deployment needs a deterministic one

level to tag mapping:

level value Tags applied
ligero, rapido, fast tech,ssl
medio, medium cve,misconfig,default-login,tech,ssl
profundo, full, deep cve,misconfig,default-login,exposure,network,ssl,tech,ot
ot, ics, industrial ot only
Use one of the listed level values

A level value not in the table above applies no tag filter at all, so the scan walks the entire template tree with only the severity filter. This is rarely what anyone intends and is dramatically slower. Check for typos.

Templates directory resolution order: the templatesDir field of the scan request, then securityProbes.vulnScan.templatesDir, then defaultTemplatesDir, then a vulnscan-templates directory next to the product binary. Relative paths resolve against the working directory.

allowTemplateUpdates behaviour:

  • true (default, including when the key is absent): best-effort refresh to the latest template release. Being offline or having GitHub blocked does not abort the scan — local templates are kept. An empty directory triggers a full download.
  • false: strict offline kill-switch. The network is never touched, even when the directory is empty. If nothing is on disk the scan fails with a clear error.
The OT suite never depends on this

The OT/ICS templates ship inside the product and the bundled set is restored after every template update, so they survive a wipe-and-replace by the template manager and work with allowTemplateUpdates: false on an air-gapped network. See OT/ICS vulnerability scanning.

allowIntrusive governs the write/control layer of the OT suite over both REST and MQTT. A request asking for intrusive mode while this is false is rejected, and the execution ends as failed with an explicit message. The scheduled probe is never intrusive regardless of this flag.

enableOT adds the read-only ot tag to the scheduled sweep even when level would not include it. It never enables the intrusive layer. Some older PLCs are fragile in the face of unexpected connections; enable it only if the OT network tolerates periodic probing.

Recommended steady state for a probe on an industrial segment:

securityProbes:
  vulnScan:
    enabled: true
    level: "medio"
    enableOT: true            # continuous read-only OT visibility
    allowIntrusive: false     # write layer bolted shut
    allowTemplateUpdates: false   # air-gapped: never reach for the network
    defaultTemplatesDir: "/opt/axiom-border/vulnscan-templates"

securityProbes.snmp

Field Type Default Description
enabled bool false Enable the scheduled probe
schedule duration 2h Interval. Zero or negative falls back to 2h
targets []string ["192.168.1.0/24"] Hosts and CIDR ranges to interrogate
mibDir string MIBS/JSON-FORMAT MIB catalogue in JSON format
mib string synology Default MIB when no device match is found
port int 161 SNMP destination port
timeout duration 2m SNMP operation cut-off
oids []string sysDescr, sysName, sysObjectID OIDs fetched by GET
walkRoot string "" Root OID for the walk. Empty means sysDescr
workers int 32 Walk parallelism. Zero or negative becomes 1
Host pre-discovery has a fixed 30 s limit

This probe uses nmap for host pre-discovery, with a fixed 30 second limit. A /24 range at T2 timing will exhaust it. Narrow the range or raise the main nmap timing value.

With neither oids nor walkRoot set there is nothing for the probe to do.

securityProbes.sniffing

Continuous traffic capture with periodic persistence to the networktraffic bucket.

Field Type Default Description
interfaces []string [] Interfaces to capture. An empty list disables sniffing — there is no separate enabled switch for this block
bpf string "" BPF filter. Empty captures all IPv4
promiscuous bool true Put the NIC in promiscuous mode
backend string pcap Capture mode. pcap is the supported value
duration duration 10m Default window for a manual capture
flushInterval duration 5m Persistence interval for continuous capture. Zero or negative becomes 1 minute

Interface naming is platform-specific and is the most common source of a probe that captures nothing:

Platform Format Example
Linux Simple name eth0, enp3s0, wlan0
macOS BSD name en0
Windows Npcap NPF device path \\Device\\NPF_{GUID}

On Windows, the friendly name (Ethernet, Wi-Fi) does not work. Discover the NPF path with nmap --iflist and read the WINDEVICE column.

opengate

Optional cloud integration: inventory reporting (collect) and provisioning (provision). The whole section is inert when enabled: false.

Field Type Default Description
enabled bool false Master switch
apiKey string "" Secret. Sent as the X-ApiKey header over HTTP, and used as the default MQTT password when collect.mqtt.password is empty
cron string */30 * * * * Five-field cron expression for provision and collect. Descriptors such as @hourly are accepted. Empty or invalid means the integration does not run
minPeriod duration 30m Throttle. If the cron interval is shorter than this, the cron is ignored and a plain ticker at minPeriod is used instead
macDiscoveryTimeout duration 10s Limit for resolving the local host MAC

opengate.collect

Field Type Default Description
enabled bool false Enable collected-data reporting
mode string mqtt http or mqtt
urlTemplate string OpenGate south collect endpoint URL template; {{deviceID}} is substituted
deviceId string "" Force a fixed device ID. Empty derives one per host from IP and MAC
sendByParts bool false Split the payload into components: ports, SNMP, vulnerabilities
partSize.ports int 100 Port rows per chunk
partSize.snmp int 100 SNMP entries per chunk
partSize.vulnerabilities int 100 Vulnerabilities per chunk
retryCount int 3 Retries for the collect publish or POST
retrySleep duration 5s Wait between retries
mqtt.broker string "" OpenGate broker URL. Required for mode: mqtt; empty skips sending
mqtt.username string "" MQTT username
mqtt.password string "" Secret. Empty falls back to opengate.apiKey
mqtt.topic string "" Publish topic, accepts {{deviceID}}. Required for mode: mqtt
address.* string Asset location metadata: country, region, province, town, postal, address

opengate.provision

Field Type Default Description
enabled bool false Enable bulk provisioning
bulkUrlTemplate string OpenGate north bulk endpoint Template with {organizationName} and {provisionProcessorId} placeholders
searchUrl string OpenGate north bulk search endpoint Endpoint for querying bulk status
organizationName string "" Target OpenGate organisation. Needed when enabled
provisionProcessorId string "" Provision processor ID. Needed when enabled
retryCount int 3 Retries for the bulk file upload
retrySleep duration 5s Wait between retries
pollMaxAttempts int 10 Maximum bulk result polls
pollSleep duration 5s Wait between polls

mqtt

Three independent blocks: broker is the MQTT broker embedded in the product, client is the internal publisher of executions and alarms, and ops is the client that listens for OpenGate operations and answers them.

mqtt.broker

Field Type Default Description
enabled bool true Start the embedded broker
host string 0.0.0.0 TCP listener interface
port int 1883 MQTT TCP port
username string "" Broker authentication. Empty username and password together allow anonymous access
password string "" Secret
ws.enabled bool true Secondary WebSocket listener, used by the web UI
ws.host string 0.0.0.0 Not configurable — the WebSocket listener always binds on all interfaces
ws.port int 1888 WebSocket port
ws.path string /ws WebSocket endpoint path. A leading slash is added if missing
ws.tls bool false TLS on the WebSocket listener
tls.enabled bool false TLS on the main MQTT listener
tls.host string "" TLS listener interface. Empty inherits broker.host
tls.port int 8883 MQTT-over-TLS port
tls.verify bool false Verify client certificates (mTLS)
tls.caFile string "" CA used to validate clients. Needed when verify is on
tls.certFile string "" Server certificate. Needed when TLS is enabled
tls.keyFile string "" Server private key. Needed when TLS is enabled
Anonymous by default

With username and password both empty the embedded broker accepts anonymous connections. On any network you do not fully control, set credentials and enable TLS. If you do enable TLS, supply a complete and valid certificate set: an incomplete TLS block prevents the broker from starting at all.

mqtt.client

Field Type Default Description
enabled bool true Start the internal publisher
broker string tcp://127.0.0.1:1883 Broker URL, by default the embedded one. Empty disables the client with a warning
clientId string "" Empty generates a unique client ID automatically
username string "" Only sent when non-empty
password string "" Secret, only sent when non-empty
topic string axiom-border/executions Execution-event publish topic. Empty publishes nothing
qos int 1 Publish QoS
retain bool true Retain flag on published messages

Auto-reconnect is always on, retrying every 5 seconds, with a 10 second initial connection timeout. Neither is configurable.

mqtt.ops

Field Type Default Description
enabled bool true Start the operations listener
broker string tcp://127.0.0.1:1883 Broker to subscribe against
username string "" MQTT username
password string "" Secret
topicSubscribe string odm/operation Incoming OpenGate operations topic
topicPublish string odm/response/{device-id} Response topic; {device-id} is substituted at runtime
qos int 1 QoS for both subscribe and publish
retain bool false Retain flag on responses

If broker, topicSubscribe or topicPublish is empty, the operations module does not start.