Skip to content

Configuration

The platform is configured through TOML files parsed by tomlplusplus. There is one file per role under config/, each driving a different subset of modules. The path is supplied to the server binary via -i. Utils::setup parses the file once at startup and exposes typed accessors UTILS::instance()->get_d(section, key) (integer) and get_s(section, key) (string).

File Layout

config/
├── prepaid.toml                # Prepaid OCS role
├── cdr_loader.toml             # CDR ingestion role
├── billing1.toml               # Billing role for bill-cycle 1
├── billing2.toml               # Billing role for bill-cycle 2
├── billing3.toml               # Billing role for bill-cycle 3
├── billing4.toml               # Billing role for bill-cycle 4
├── prepaid_mass_rating.toml    # Developer-only mass rating role
├── crm.toml                    # CRM gRPC role
├── stats.toml                  # Stats handler role
├── bill_formatter.toml         # PDF bill formatter role
├── cdr_loader.toml             # CDR ingestion (alias also above)
├── cdr_generator.toml          # Tool config — bin/cdr_generator
└── diameter_client.toml        # Tool config — bin/diameter_client

The cdr_generator.toml and diameter_client.toml files configure standalone tools, not the server binary itself.

Common Sections

These sections appear (with the same semantics) in nearly every role's TOML.

[modules]

Boolean (0/1) gate per platform module. Read by main.cpp. The license feature mask still has final say, but [modules] is what enables the wiring at all:

[modules]
billing             = 0
prepaid             = 1
prepaid_mass_rating = 0
cdr_loader          = 0
stats               = 0
crm                 = 0
bill_formatter      = 0

The reference role files each enable exactly one module (with the exception of prepaid_mass_rating.toml, which enables both billing and prepaid_mass_rating).

[system]

[system]
thread_pool_size  = 8
timestamp_format  = "%Y-%m-%d %H:%M:%S"
time_zone         = "Europe/Bucharest"
ssl               = 0   # only present in prepaid.toml; reserved for the prepaid acceptor

thread_pool_size is consumed by THREAD_POOL::instance()->init(size) in main.cpp. timestamp_format is used for both Utils::to_datetime and Utils::from_datetime.

[db]

[db]
username = "macbook"
password = "..."
host     = "localhost"
port     = 33060          # MySQL X DevAPI default port
database = "billing"
poolsize = 15

Every role connects to the same MySQL instance via mysqlcppconnx (X DevAPI). Each worker thread opens its own session — poolsize is currently advisory; X DevAPI manages session pooling internally.

[logging]

See Logging for full semantics.

[app]

[app]
operator_id                      = 3
free_units_functionality         = 1
initial_delay                    = 0
interval                         = 60
activity_heartbeat_stale_s       = 600
activity_heartbeat_interval_subs = 10

operator_id filters reference-data queries to the active operator. free_units_functionality toggles free-unit accounting in the rater. initial_delay and interval are passed to ACE_Reactor::schedule_timer for the timer-driven handlers (CDR loader, billing, stats, bill formatter); units are seconds.

activity_heartbeat_stale_s is the watchdog threshold consulted by BillingHandler::handle_timeout (see Postpaid Billing). A PROCESSING activity whose heartbeat_at is older than this many seconds is presumed dead and is transitioned to ACTIVITY_STATUS_ERROR on the next tick. The default of 600 seconds is generous so a long bill run on limited hardware does not trip a false positive; setting it to 0 disables the watchdog entirely.

activity_heartbeat_interval_subs is the cadence at which DB_layer::billCycle refreshes the heartbeat inside its per-subscriber loop. One beat per ten subscribers (the default) keeps heartbeat_at comfortably fresh without hammering the database; setting it to 0 heartbeats only at the start and end of the run.

[license]

[license]
license_key = "./license/platform.lic"
lkgt_path   = "./license/.lkgt"

Required in every role. Validated before any module is initialised.

Role-Specific Sections

prepaid.toml

[server]
port = 10100

The DIAMETER acceptor listens here. The [system].ssl flag is reserved for an SSL-wrapped DIAMETER mode that requires __SSL_MODE__ to be defined at compile time; the default build is non-SSL.

billing*.toml

[billing]
billcycle = 1   # 2 in billing2.toml, 3 in billing3.toml, 4 in billing4.toml

BillingHandler::setBillcycle is called with this value. Activities whose bill-cycle does not match are silently rejected by the running process, so each bill-cycle has at most one process draining it.

cdr_loader.toml

[cdr]
cdr_path                  = "/.../server.micro.bss/cdr"
cdr_prefix                = "data"
new_cdr_path              = "/.../cdr/new"
processed_cdr_path        = "/.../cdr/processed"
error_cdr_path            = "/.../cdr/error"
duplicate_cdr_path        = "/.../cdr/duplicate"
new_cdr_extension         = ".cdr"
processed_cdr_extension   = ".done"
error_cdr_extension       = ".error"
duplicate_cdr_extension   = ".duplicate"

new_cdr_path is polled on every tick. Files matching new_cdr_extension are processed and renamed with the appropriate suffix. Files whose CDR id is already present in the offline table land in duplicate_cdr_path with the .duplicate suffix rather than error/ — a re-presented CDR is expected upstream-mediation behaviour, not an error. The paths are absolute; on a fresh deployment the update_toml.sh helper rewrites them to point at the deployment prefix.

cdr_generator.toml

[generator]
gen_voice_min  = 3
gen_voice_max  = 30
gen_data_min   = 1024
gen_data_max   = 10240
gen_sms_units  = 1
gen_mms_units  = 1
gen_date_start = "2025-06-01 00:00:00"
gen_date_end   = "2026-02-01 00:00:00"
gen_b_number   = "40747498888"

Used by bin/cdr_generator to drive synthetic load.

crm.toml

[server]
port = 50051
host = "0.0.0.0"

[tls]
cert_path     = "certs/server.crt"
key_path      = "certs/server.key"
ca_cert_path  = "certs/ca.crt"
allow_insecure = 0

[jwt]
secret   = "your-secret-key-change-this-in-production"
issuer   = "crm-server"
audience = "crm-api"

tls.allow_insecure defaults to 0; the gRPC server refuses to start in cleartext unless explicitly authorised. The shipped jwt.secret is a placeholder — replace before deployment.

bill_formatter.toml

[bill_formatter]
template_dir = "./html"
output_dir   = "./invoices"
billcycle    = -1
year         = -1
month        = -1

-1 on the period filters means no filter; the formatter picks up any bill with status BILL_STATUS_NEW. The three filter fields are present for future scoping (e.g. running a one-shot formatter for a single archived month) but not currently consulted by BillFormatterHandler.

diameter_client.toml

See Prepaid Charging. Drives the bin/diameter_client load tester: connection target, execution mode, MSISDN pools, per-service ranges, and event-type distribution.

stats.toml

stats.toml enables only the stats module and inherits the common timer fields from [app]. StatsHandler::handle_timeout queries the database aggregates and writes the four stat rows backing the CRM dashboard charts.

Updating Paths after Deployment

scripts/update_toml.sh <new_root> rewrites the absolute paths embedded in TOML files (log paths, CDR directories, SSL paths) to match the runtime deployment prefix. It is invoked automatically by scripts/install.sh install and by unpack.sh after a tarball deployment, so operators rarely need to edit TOML by hand.