About Mobius4
August 11, 2026 · View on GitHub
Mobius4 is the next version of Mobius which basically implements the global IoT middleware standard, oneM2M. This new version provides the new code base with modern Javascript async-await syntax for better readibility and maintenance. Also, the database has been changed from MySQL to PostgreSQL with PostGIS.
In oneM2M Release 5, TR-0071 is defining candidate solutions for AIoT applications. Mobius4 implements those features in advance before the public release so developers can try them.

oneM2M Certificate
Mobius4 is the first product to be certified as a oneM2M Release 2 compliant Common Services Entity (CSE, also known as an IoT platform). By the configuration, it runs as ASN/MN-CSE as well as IN-CSE.

Supported oneM2M features
Two generated files sit alongside this prose and are worth reading when the answer has to be precise:
features/capabilities.jsonrecords what a running instance actually answered (regenerated bynpm run probe-capabilities, checked in CI), andfeatures/support-matrix.mdshows how far the implementation reaches across the standard, area by area.
Mobius4 implements oneM2M Common Services Entity (CSE) which is the IoT middleware. By the configuration, it runs as ASN/MN-CSE as well as IN-CSE.
oneM2M protocol bindings:
- HTTP
- MQTT — as a server, and outbound to any broker a notificationURI or a
<remoteCSE>'s pointOfAccess names, not only the one this CSE listens on
oneM2M primitive serialization:
- JSON
oneM2M resource types (until Release 4):
- CSEBase, AE, remoteCSE
- accessControlPolicy
- container, contentInstance, latest, oldest
- flexContainer (specializations; how to use · configuration)
- subscription
- group, fanOutPoint
oneM2M resource types (oneM2M TR-0071, next release):
- modelRepo, mlModel, modelDeployList, modelDeployment
- mlDatasetPolicy, dataset, datasetFragment
Other features:
- discovery with Filter Criteria parameter
- geo-query with location common attribute (Rel-4 feature)
- children resources retrieval with Result Content parameter — nested representations
(
rcn=4/rcn=8) and child resource references (rcn=5/rcn=6), paginated with Filter Criterialvl/lim/ofst. The same four values work on DELETE, returning what was removed
Platform features
Beyond the oneM2M standard, Mobius4 includes the following operational capabilities for production deployments.
Observability:
- Structured JSON logging via Pino with daily log rotation (
logging.*) - Health check endpoint
GET /health— for load balancers and container liveness probes - Prometheus-compatible metrics endpoint
GET /metrics(disabled by default; enable viametrics.enabled)
Security:
- HTTP security headers via Helmet (disabled by default; enable via
security.helmet.enabled) - Per-IP rate limiting (disabled by default; enable via
security.rateLimit.enabled)
Resilience:
- Graceful shutdown on
SIGTERM/SIGINT— ordered teardown of HTTP, MQTT, and database connections with a 30-second forced-exit fallback - MQTT exponential backoff reconnection — configurable initial delay, multiplier, jitter, and max attempts (
mqtt.reconnect.*)
Operations:
- Local configuration override via
config/local.json(gitignored) — credentials and environment-specific settings never committed - PM2 process management via
ecosystem.config.js— auto-restart, environment profiles, graceful stop integration
Postman scripts
Try oneM2M APIs over HTTP binding with Postman client. You can download Postman script collection and import it on your Postman. There are two variables set in the collection mp_url for Mobius4 platform URL and cb for CSEBase resource name, so please add in your Postman variable settings.
How-to documents
There are some modifications from the previous version so please check Mobius4 how-to for the Mobius developers. If you're trying the new oneM2M features on AI, check Rel-5 features how-to.
For developers adding new oneM2M resource types to Mobius4: Adding a new resource type — covers every file to create or modify (enums, model, DDL, CRUD handler, dispatch switches, discovery maps, validation schema) with copy-paste code patterns.
For upgrading an existing deployment: Upgrading Mobius4 — the required steps (DB migrations, new prerequisites) and the known upgrade problems, per version. None of it applies to a clean install.
Running Mobius4
Prerequisites
Since Mobius4 is developed with Node.js and PostgreSQL, any operating system that supports them can run Mobius4.
- Node.js v22 or v24 — both are supported (CI runs both on every change; see
engines). New installs should use v24, the current LTS and this repository's development default (see .nvmrc); existing v22 deployments continue to work unchanged. - PostgreSQL v17 — developed and CI-tested on 17.4.
- PostGIS v3.6 — required, not optional.
db/init.jsdeclaresGEOMETRY(GEOMETRY, 4326)columns on the resource tables, so schema creation fails without the extension even if you never issue a geo-query. Developed on 3.6.4; CI runs thepostgis/postgis:17-3.6-alpineimage. 3.x releases below 3.6 are expected to work but are not tested here. Enable it per database withCREATE EXTENSION postgis;. - MQTT broker (e.g. Mosquitto)
With Docker, there is nothing on this list to install and no database to create by hand:
cp .env.example .env && docker compose up -d brings up Mobius4, PostgreSQL with PostGIS and an
MQTT broker together — see docs/docker.md.
For OS-specific installation instructions (Windows, macOS, Linux): docs/installation.md
Installation
-
Create a database named
mobius4on PostgreSQL -
Get Mobius4 source codes from this git repository
git clone https://github.com/iotketi/mobius4
- Install node packages in the 'mobius4' folder
cd mobius4
npm install
If you manage Node.js versions with nvm, run nvm use inside this folder first — it picks up the version pinned in .nvmrc (v24) automatically.
- Set Mobius4 configuration file
cp config/local.json.example config/local.json
# edit config/local.json with your DB credentials and local settings
- Run Mobius4
node mobius4.js
Configurations
Full configuration reference: docs/configuration.md
For deployment details (health check, metrics endpoint, PM2, resource browser): docs/operations.md
To serve the HTTP binding over TLS — obtaining a certificate, installing it, replacing it before it expires, and what it does and does not prove about the client: docs/tls.md
Contact
Version history
Mobius4 source code
Full detail for every release is in CHANGELOG.md. The Upgrading column links to what an existing deployment has to do — required steps and known upgrade problems. A clean install needs none of it.
| Version | Date | Description | Upgrading |
|---|---|---|---|
| 4.0.0 | 2025-09-22 | Initial release of Mobius4 | — |
| 4.1.0 | 2026-03-13 | oneM2M Rel-2 certification | — |
| 4.2.0 | 2026-04-05 | logging module update | — |
| 4.3.0 | 2026-04-09 | performance improvements | — |
| 4.4.0 | 2026-04-19 | conformance updates for performance improvements | DB migration required |
| 4.4.1 | 2026-08-01 | Node.js 22/24 CI, dead dependency cleanup, DAS/jose removal, installation docs update | Node 24 notes |
| 4.5.0 | 2026-08-02 | <flexContainer> (ty=28) with a specialization registry; response-status fallback in the HTTP binding | DB migration required |
| 4.5.1 | 2026-08-02 | MQTT binding test coverage | test prerequisite |
| 4.6.0 | 2026-08-02 | Breaking: cse.admin has no default and SM is refused; the administrator's privileges now come from an <accessControlPolicy> rather than a bypass; resourceName is checked against its ABNF. Closes a full access-control bypass | Will not start until configured; DB migration required |
| 4.6.1 | 2026-08-05 | Conformance: <CSEBase> UPDATE/DELETE answer 4005 for every originator; the group fanout member is named m2m:rsp | client-side check |
| 4.6.2 | 2026-08-05 | <contentInstance> creation and retention each become a single SQL statement — roughly 3.3× the write throughput, 2.5× with retention active; stateTag no longer collides under concurrent creates | — |
| 4.6.3 | 2026-08-05 | db.pool.max is the connection total for the process rather than a figure each of two pools applied separately; default 30 → 20. Unblocks running more than one instance | only if you overrode db.pool.max |
| 4.6.4 | 2026-08-05 | A name that is already taken is refused with 4105 rather than 4000, including under concurrency; MQTT subscription and expired-resource cleanup run on one instance | — |
| 4.6.5 | 2026-08-06 | Conformance: a <contentInstance> under a <container> carrying an <accessControlPolicy> was refused to every originator, the administrator included, and was missing from discovery results — it now follows the parent's policy as TS-0001:9.6.7 requires. Discovery decides access once per policy holder instead of once per resource: 18 → 614 requests per second over 150 content instances. Development logging settings carried into a deployment now say so at startup | workarounds you can undo |
| 4.7.0 | 2026-08-07 | HTTPS is now optional and off by default — an existing deployment must set https.enabled and point https.key/https.cert at its own files to keep serving TLS. The listener no longer asks clients for certificates: it set requestCert but nothing ever read the certificate, so it never proved the originator. The certificates this repository shipped are deleted and must be treated as disclosed | Set https.enabled to keep TLS; reissue the shipped keys |
| 4.8.0 | 2026-08-07 | docker compose up brings up mobius4, PostgreSQL/PostGIS and an MQTT broker in one command — see docs/docker.md. No change for an existing source deployment | — |
| 4.9.0 | 2026-08-07 | A <container>'s maxInstanceAge now actually caps its <contentInstance> children's expirationTime (TS-0004:7.4.7.2.1 step 2 e), and its default widens from 30 to 365 days so a default container keeps behaving as before; new maxByteSizePerInstance (mbis) refuses oversized content independently of maxByteSize | DB migration required |
| 4.10.0 | 2026-08-07 | Breaking for rcn=4/rcn=8 clients: child resources are now nested inside their own parent instead of grouped by type at the top level (TS-0004:8.4.3 EXAMPLE 3), and lim cuts on subtree boundaries while ofst counts direct children. Both fail silently against an old client. rcn=5/rcn=6 are implemented — they previously returned attributes only, with RSC 2000. Truncated child-resource results now set X-M2M-CTS/X-M2M-CTO | Client-side changes required |
| 4.11.0 | 2026-08-08 | contentSize now counts bytes (TS-0001:9.6.7) instead of JavaScript string units — a 10-byte payload was being refused by a maxByteSizePerInstance of 10, and cbs/mbs/sizeAbove/sizeBelow read the same figure. A database failure answers 5000 instead of 4004/4000/4103, so a client retries rather than trying to recreate live resources. /health became a readiness check and now fails when the database is unreachable. <subscription> sets creator and notifications carry it (TS-0004:7.4.8.2.1, 7.5.1.2.2); creator can no longer name another entity | Read before upgrading — contentSize values change |
| 4.11.1 | 2026-08-08 | Forwarding to a <remoteCSE> kept the remote CSE's response status instead of replacing it with 2000, and now tries every pointOfAccess before answering 5103 TARGET_NOT_REACHABLE; an mqtt: access point is refused rather than reported as success. A generated resourceName is checked for collision before use. <AE> mandatory-attribute validation moved into the Joi schema. Dead module cse/routing.js removed | — |
| 4.12.0 | 2026-08-08 | DELETE honours rcn 4/5/6/8 (TS-0001:8.1.2 Table 8.1.2-1) — the response can now carry the child resources, or references to them, as they were just before removal. Notifications go to the broker their mqtt:// URL names instead of always to this CSE's own (TS-0010:6.6.2/6.6.4), and a <remoteCSE> whose pointOfAccess is mqtt:// can now be forwarded to (TS-0010:6.4.2/6.4.3). All additive | — |
| 4.13.0 | 2026-08-08 | Group members hosted on another CSE now work. A member the CSE did not host resolved to resource type 0, was dropped by the default consistency strategy, and the group was still returned with memberTypeValidated = true — it now retrieves the member's type from its Hosting CSE and distinguishes readable / no-privilege / unreachable as TS-0004:7.4.13.2.1 requires. <AE> accepts ontologyRef, and an <AE> UPDATE no longer discards contentSerialization. A fanout over a group with no members answers 4109 NO_MEMBERS instead of 2000 with an empty list. Forwarded responses carry the Response Status Code as a number, not a string. Conformance tests are now transcribed from TS-0018 test purposes (209 → 282) | DB migration required |
| 4.13.1 | 2026-08-08 | A containerised CSE could not be told to register with another CSE: docker/entrypoint.js assembled NODE_CONFIG without a cse.registrar block and without cse_type, and it overwrites NODE_CONFIG, so the settings could not be injected from outside either. Six optional variables added, plus a working two-CSE compose example. No change for a standalone deployment | — |
| 4.14.0 | 2026-08-11 | Expired resources stop acting like live ones before the sweep deletes them: an expired <subscription> no longer publishes notifications, and an obsolete <contentInstance> is no longer served by <latest>/<oldest> or listed among rcn=4/rcn=8 children (TS-0001:10.2.4.4) — which also means maxInstanceAge is now enforced on reads, not only on writes. The sweep runs at startup, so a deployment restarting more often than its interval no longer skips it entirely. Breaking if you compute ofst yourself: the offset filter is 1-based, so ofst=1 is the first result rather than the second (TS-0004:7.3.3.17.15); X-M2M-CTO moves with it and is no longer ever 0. Internal error text no longer reaches clients in m2m:dbg | Only if you compute ofst |