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.

Mobius4

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. Rel-2 Certificate

Supported oneM2M features

Two generated files sit alongside this prose and are worth reading when the answer has to be precise: features/capabilities.json records what a running instance actually answered (regenerated by npm run probe-capabilities, checked in CI), and features/support-matrix.md shows 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 Criteria lvl/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 via metrics.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.js declares GEOMETRY(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 the postgis/postgis:17-3.6-alpine image. 3.x releases below 3.6 are expected to work but are not tested here. Enable it per database with CREATE 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

  1. Create a database named mobius4 on PostgreSQL

  2. Get Mobius4 source codes from this git repository

    git clone https://github.com/iotketi/mobius4
  1. 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.

  1. Set Mobius4 configuration file
cp config/local.json.example config/local.json
# edit config/local.json with your DB credentials and local settings
  1. 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

iotketi@keti.re.kr

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.

VersionDateDescriptionUpgrading
4.0.02025-09-22Initial release of Mobius4
4.1.02026-03-13oneM2M Rel-2 certification
4.2.02026-04-05logging module update
4.3.02026-04-09performance improvements
4.4.02026-04-19conformance updates for performance improvementsDB migration required
4.4.12026-08-01Node.js 22/24 CI, dead dependency cleanup, DAS/jose removal, installation docs updateNode 24 notes
4.5.02026-08-02<flexContainer> (ty=28) with a specialization registry; response-status fallback in the HTTP bindingDB migration required
4.5.12026-08-02MQTT binding test coveragetest prerequisite
4.6.02026-08-02Breaking: 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 bypassWill not start until configured; DB migration required
4.6.12026-08-05Conformance: <CSEBase> UPDATE/DELETE answer 4005 for every originator; the group fanout member is named m2m:rspclient-side check
4.6.22026-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.32026-08-05db.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 instanceonly if you overrode db.pool.max
4.6.42026-08-05A 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.52026-08-06Conformance: 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 startupworkarounds you can undo
4.7.02026-08-07HTTPS 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 disclosedSet https.enabled to keep TLS; reissue the shipped keys
4.8.02026-08-07docker 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.02026-08-07A <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 maxByteSizeDB migration required
4.10.02026-08-07Breaking 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-CTOClient-side changes required
4.11.02026-08-08contentSize 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 entityRead before upgrading — contentSize values change
4.11.12026-08-08Forwarding 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.02026-08-08DELETE 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.02026-08-08Group 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.12026-08-08A 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.02026-08-11Expired 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:dbgOnly if you compute ofst