Mobius4 Configuration Reference

August 5, 2026 · View on GitHub

All settings live in config/default.json. Override locally with config/local.json — it is loaded automatically with higher priority and is gitignored, so credentials never get committed.

Local configuration override

Setup (first time):

cp config/local.json.example config/local.json
# then edit config/local.json with your actual values

Typical config/local.json for development:

{
  "cse": {
    "sp_id": "//your-domain.io",
    "poa": ["http://YOUR_SERVER_IP:7599"]
  },
  "db": {
    "user": "your_db_user",
    "pw": "your_db_password"
  },
  "logging": {
    "level": "debug",
    "console": { "pretty": true }
  }
}

The logging block above is for development only. Pretty printing costs about 18% of throughput and debug a further 7% (measured — see Logging guide → Throughput cost). Drop the block when deploying; mobius4 warns at startup if it is still present.

Typical config/local.json for production deployment:

{
  "cse": {
    "sp_id": "//your-domain.io",
    "poa": ["http://YOUR_SERVER_IP:7599"]
  },
  "db": {
    "user": "mobius4",
    "pw": "strong_password_here"
  },
  "logging": {
    "level": "info",
    "file": { "enabled": true }
  },
  "security": {
    "helmet": { "enabled": true },
    "rateLimit": { "enabled": true, "max": 500 }
  }
}

Configuration keys

CSE

keydescription
cse.cse_typeCSE Mode (1: IN, 2: MN, 3: ASN)
cse.sp_idM2M Service Provider ID — must start with //
cse.cse_idCSE ID — must start with /
cse.csebase_rnResource name of the CSEBase resource
cse.poaPoint of access of this CSE
cse.registrar.cse_typeCSE type of the Registrar (registration target)
cse.registrar.cse_idCSE ID of the Registrar
cse.registrar.csebase_rnCSEBase resource name of the Registrar
cse.registrar.ipIP address of the Registrar
cse.registrar.portPort number of the Registrar
cse.registrar.versionsSupported oneM2M versions of the Registrar
cse.adminRequired, no default. Identity the admin <accessControlPolicy> grants all six operations to. See the warning below
cse.aeid_lengthString length of AE ID
cse.expired_resource_cleanup_interval_daysInterval for expired resource cleanup in days
cse.discovery_limitMax number of resource IDs in a discovery response
cse.allow_discovery_for_anyIf true, access control is skipped for discovery (faster responses)
cse.keep_alive_timeoutHTTP keep-alive session timeout in seconds

Sizing db.pool.max for more than one instance

db.pool.max is the total for the process, not a per-pool figure. mobius4 runs two connection pools — Sequelize's for the models and a raw pg pool for the hand-written SQL — and the setting is divided between them.

This matters when planning capacity. PostgreSQL's default max_connections is 100, so the number of instances you can run is bounded by max_connections / db.pool.max. At the default of 20, that is five instances before the server starts refusing connections with too many clients.

Raising it buys very little. Measured at a concurrency of 100 on the development machine: 10 connections per pool reached 3,069 requests per second against 3,139 for 30 — a 2% difference for three times the connections. Prefer spending the connection budget on instances rather than on pool size.

⚠️ cse.admin is a privileged identity

cse.admin names the identity that the admin <accessControlPolicy> (cb.admin_acp.rn, created at startup) grants all six operations to. Whoever knows this value and can reach the port has whatever that policy allows, on every resource that carries it — over plain HTTP exactly as over TLS. Treat it as a credential.

Up to v4.5.1 it was worse than that: cse/hostingCSE.js granted the identity every operation before any <accessControlPolicy> was consulted, on every resource regardless of policy. That short-circuit is gone as of v4.6.0 — the administrator now reaches a resource only through a policy that names it, or through the creator fallback when the resource carries no acpi.

Because of that there is no default. Up to v4.5.1 the shipped value was SM, which meant every deployment that never overrode it could be taken over by anyone who had read this repository. Since v4.6.0 mobius4 refuses to start when cse.admin is missing, blank, or set to SM.

Choose a value unique to this deployment and treat it as a credential. If you are upgrading from v4.5.1 or earlier, the old identity is also recorded in the database — in every resource's cr/int_cr and in the default ACP's privileges — so run db/migrations/v4.6.0.sql after changing the configuration. Changing the configuration alone leaves the new admin unable to modify the default <accessControlPolicy>.

CSEBase

keydescription
cb.default_acp.rnResource name of the default accessControlPolicy resource
cb.default_acp.createAllow Create privilege on the default ACP
cb.default_acp.retrieveAllow Retrieve privilege
cb.default_acp.updateAllow Update privilege
cb.default_acp.deleteAllow Delete privilege
cb.admin_acp.rnResource name of the admin <accessControlPolicy>, which grants cse.admin all six operations (acop 63)
cb.default_acp.discoveryAllow Discovery privilege

HTTP / HTTPS

keydescription
request.max_body_sizeMax HTTP request body size (default: 1mb)
http.portHTTP server port (default: 7599)
https.portHTTPS server port (default: 7580)

MQTT

keydescription
mqtt.enabledEnable MQTT binding (default: true; set false to run HTTP-only)
mqtt.ipMQTT broker IP address
mqtt.portMQTT broker port number (default: 1883)
mqtt.initialConnectTimeoutMsStartup wait for MQTT broker in ms (default: 10000). On timeout, continues HTTP-only with background reconnect.
mqtt.reconnect.initialDelayMsFirst reconnect wait time in ms (default: 1000)
mqtt.reconnect.maxDelayMsUpper bound of reconnect delay in ms (default: 60000)
mqtt.reconnect.multiplierBackoff multiplier applied on each failure (default: 2)
mqtt.reconnect.jitterRandom variance factor ±jitter applied to delay (default: 0.2 = ±20%)
mqtt.reconnect.maxAttemptsMax reconnect attempts. 0 = unlimited (default: 0)

Database

keydescription
db.hostPostgreSQL host address
db.portPostgreSQL port number (default: 5432)
db.nameDatabase name (default: mobius4)
db.userDatabase user name
db.pwDatabase user password
db.pool.maxPostgreSQL connections one mobius4 process may open, in total (default: 20). Split evenly between the two pools this process runs — see below
db.pool.idleTimeoutMsHow long an unused connection stays open before being closed (default: 30000)
db.pool.connectionTimeoutMsHow long a request waits for a free connection, and for a new one to be established, before failing (default: 2000)
db.pool.statementTimeoutMsServer-side cap on a single statement (default: 30000)

Logging

keydescription
logging.levelLog level: trace, debug, info, warn, error, fatal (default: info)
logging.console.enabledEnable console output (default: true)
logging.console.prettyHuman-readable output for development (default: false). Costs about 18% of throughput and is ignored when NODE_ENV=production
logging.file.enabledEnable file logging (default: true)
logging.file.pathLog file path (default: logs/mobius4.log)
logging.file.rotateRotation frequency: daily or hourly (default: daily)
logging.file.maxFilesNumber of rotated files to keep (default: 14)
logging.file.maxSizeMax file size before rotation (default: 100m)

See logging-guide.md for structured logging details.

Security

keydescription
security.helmet.enabledEnable HTTP security headers via Helmet (default: false)
security.rateLimit.enabledEnable per-IP rate limiting (default: false; disable for load/performance tests)
security.rateLimit.windowMsRate limit window in milliseconds (default: 60000)
security.rateLimit.maxMax requests per window per IP (default: 500)

Metrics

keydescription
metrics.enabledEnable Prometheus /metrics endpoint (default: false). Keep disabled during load/performance testing.

Advanced

These settings control internal ID and data size limits. The defaults work for most deployments.

Length constraints

keydescription
length.entity_idMax length of AE and CSE ID
length.riLength of resourceID (ri) attribute
length.piLength of parentID (pi) attribute
length.rn_randomLength of random part of resourceName when not given by Originator
length.rnMax length of resourceName (rn)
length.structured_res_idMax length of structured resource ID (e.g. Mobius/cnt1)
length.str_tokenLength of each string token (e.g. cnt1)
length.urlMax URL length (e.g. pointOfAccess attribute)
length.dataMax data length (e.g. datasetFragment attribute)

Default resource values

keydescription
default.common.et_monthDefault resource expiration in months (current time + et_month)
default.container.mbsDefault maxByteSize of a container resource
default.container.mniDefault maxNumberOfInstances of a container resource
default.container.miaDefault maxInstanceAge of a container resource
default.datasetPolicy.tcdDefault time correlation duration for dataset creation
default.datasetPolicy.nvpDefault null value policy for dataset creation
default.datasetPolicy.nrhdDefault number of rows in historical dataset
default.datasetPolicy.nrldDefault number of rows in live dataset

flexContainer specializations

A <flexContainer> (ty=28) carries [customAttribute] members whose names and types are defined by the document its cnd (containerDefinition) attribute points at, not by oneM2M. TS-0004:7.4.37.2.1 requires the CSE to validate a request against that schema and to answer SPECIALIZATION_SCHEMA_NOT_FOUND (4125, HTTP 501) when the schema is unavailable.

Mobius4 declares that contract locally instead of fetching an XSD on the CREATE path. The registry lives in its own file, config/specializations.json, read directly by cse/specialization.js — it is not part of the config settings tree, so NODE_ENV and local.json layering do not apply to it. The whole file is the registry, keyed by cnd URI:

{
  "http://developers.iotocean.org/schema/parkingBlock.xsd": {
    "typeName": "parkingBlock",
    "namespacePrefix": "sc",
    "attributes": {
      "type":                { "type": "string"  },
      "category":            { "type": "array"   },
      "availableSpotNumber": { "type": "integer" },
      "totalSpotNumber":     { "type": "integer" }
    }
  }
}

It is a separate file because it describes the deployment's information model rather than CSE settings, and it grows one entry per specialization. Adding one therefore never touches default.json, and the two can be reviewed and deployed independently.

keydescription
<cnd URI>The exact cnd value clients will send. An unregistered value is rejected with 4125
typeNameLocal name of the specialization, e.g. parkingBlock
namespacePrefixNamespace prefix of the envelope key. TS-0004:7.4.37.1 allows a specialization to use a targetNamespace other than m2m:
attributesAllowed [customAttribute] names and their types. Supported types: string, integer, number, boolean, array, object

The envelope key of a request must be exactly namespacePrefix:typeName — for the entry above, {"sc:parkingBlock": {...}}. Custom attribute names are matched as they appear on the wire; no long-name/short-name translation is applied, because TS-0004:8.2.1 confines the short-name tables to oneM2M-defined names and a third-party specialization has none.

All custom attributes are optional (TS-0004:7.4.37.1 lists [customAttribute] as O/O). A name that is not declared, or a declared name carrying the wrong type, is rejected with 4000.

Adding a specialization means editing config/specializations.json and restarting the CSE; the file is read once at startup. If it is missing or unparseable the CSE still starts, logs a warning, and answers every cnd with 4125 — a deployment that uses no <flexContainer> does not need the file at all.

For a walkthrough of registering a specialization and then creating, updating, discovering and deleting resources against it, see How to — flexContainer specializations.

Not implemented: <flexContainerInstance> (ty=58). TS-0004:7.4.37.2.1 step 3 makes instance creation conditional on a non-zero mni, mbs or mia, so those three attributes are rejected with 5001 rather than stored. Consequently <latest> and <oldest> are not available under a <flexContainer> either — TS-0001:9.6.35 scopes them to the case where instances are being created.