Building from source

Most people never need this page: the published image runs the broker without a compiler anywhere in sight — see Quick start. Build from source when you want to change coraine, to run it somewhere no image suits, or to compile features out (below).

Where things live

coraine/
├── src/
│   ├── app/coraine/        # main(), arg table, plugin wiring, NGSI-LD service map
│   ├── lib/
│   │   ├── plugin/          # pluginLoader.c + ApiPlugin.h  (the loader)
│   │   ├── db/              # DbDriver.h  (current-state plugin contract) + tenant
│   │   ├── troe/            # TroeDriver.h (temporal plugin contract) + dispatch
│   │   ├── serviceRoutines/ # NGSI-LD endpoint handlers
│   │   ├── linkedEntities/  # join / linked-entity support
│   │   ├── forwarding/      # distributed-ops (CSR) forwarding
│   │   └── metrics/         # Prometheus via kprom
│   └── plugins/
│       ├── currentState/    # mongoc, corDB   (DB plugins)
│       ├── temporal/        # none, ramdb, timescale  (TRoE plugins)
│       ├── api/admin/       # admin API plugin
│       └── shared/          # geoMatch.c etc. shared across plugins
├── test/funcTests/          # corTest functional tests
├── doc/                     # plugin architecture, functest coverage audit
├── CMakeLists.txt           # real build (feature flags, lib wiring)
└── makefile                 # convenience wrapper (release/debug/install/test/coverage)

coraine links a constellation of sibling repos (k-libs + Cor-Libs) plus several system libraries. The repos must sit as siblings under one parent (default ~/git), because the build references ../<lib>/lib<lib>.a.

Fastest path — bootstrap script

If you're starting from scratch, clone the corLibs umbrella and run its bootstrap.sh: it clones every dependency as a sibling at its pinned version and builds the whole lib stack. It works wherever you put it - the layout is derived from the umbrella's own location, not from a fixed path.

git clone git@github.com:SEAMWARE/corLibs.git
./corLibs/bootstrap.sh

Then:

cd ~/git/coraine
make di            # debug build + install (binary + plugins → /opt/seamware, /usr/local/bin)

Dependency stack

  • k-libs (gitlab.com/kzangeli): kbase kalloc klog khash kjson kargs ktrace kprom
  • Cor-Libs (github.com/SEAMWARE): corRest corNgsild corJsonld corPlugin
  • umbrella / test runner: corLibs, corTest

make auto-rebuilds corRest/corNgsild/corJsonld (the broker's libs target); the k-libs and corPlugin must already be built (the umbrella or bootstrap handles that).

System packages (Debian/Ubuntu)

Need Package
HTTP server libmicrohttpd-dev
TLS libssl-dev
MQTT (notifications) libmosquitto-dev
Geo queries libgeos-dev
MongoDB driver (mongoc plugin) mongo-c v2 (mongoc2.pc via pkg-config)
TimescaleDB plugin libpq-dev
Toolchain cmake build-essential

Don't need Mongo? Build without it: cmake -DCOR_FEATURE_MONGOC=OFF and run with --database corDB. The mongo-c v2 driver is the most common build snag.

Make targets

Target Effect
make / make release Release build (BUILD_RELEASE/)
make debug Debug build (BUILD_DEBUG/)
make i / make di release/debug + install
make ci / make cdi clean + the above
make install copy broker + plugins → /usr/local/bin, /opt/seamware/plugins, /opt/seamware/etc
make clean remove build trees
make test run the functional test suite (corTest)
make coverage coverage report per DB (coverage-<db>/index.html)
make coverage-etsi full ETSI TP suite coverage (coverage-etsi/index.html)

Install writes to /opt/seamware/... and /usr/local/bin — run with appropriate permissions or pre-create the dirs.

Compiling out what you don't need

CMakeLists.txt exposes COR_FEATURE_* options — subscriptions, registrations, geoq, scopes, datasetId, multi-type, context download/hosting, tenants, mongoc, admin API, metrics, ICU collation, geo-dispatch on location/observationSpace/operationSpace. All default ON except the observation/operation-space dispatch; toggle with cmake -DCOR_FEATURE_X=OFF.

The intent is a broker you can shrink to exactly the NGSI-LD you actually deploy — no subscription engine on a read-only edge node, no geo, no tenants, no Mongo.

Where it stands today, honestly: three of them work, the rest are declared and not implemented.

-DCOR_FEATURE_MONGOC=OFF builds a Mongo-free tree (drop libmongoc from the build host, run --database corDB).

-DCOR_FEATURE_REGISTRATIONS=OFF drops the Context Source Registration, registration-subscription and EntityMap service routines, the forwarding library, and both DB plugins' registration code — about 24 kB of .text.

-DCOR_FEATURE_SUBSCRIPTIONS=OFF drops the subscription CRUD, the distributed-subscription notification receiver, the subscription code in both DB plugins and the admin plugin's subStats/flush — about 19 kB. The two are independent switches: a build can carry a complete Context Source Registration API and no subscriptions at all.

In each case the routes keep their entry in the service table and answer

HTTP/1.1 501 Not Implemented

{
  "type":   "https://coraine.readthedocs.io/errors/NotAvailableInThisDeployment",
  "title":  "Not Available In This Build",
  "status": 501,
  "detail": "'POST /ngsi-ld/v1/csourceRegistrations' is not included in this build of coraine"
}

deliberately not a 404. A 404 says the resource is not there and invites the client to fix its URL; this says the deployment declined the capability and the client's move is a different deployment. A plugin's own routes answer the same way — the admin plugin's subStats/flush is compiled out with subscriptions and returns the same 501. The type URI is ours rather than an ETSI one because TS 104-176 § 6.3.2 registers no error type for a build-time omission — the one 501 in that table, NoMultiTenantSupport, is reserved for a single capability. See spec-doubt #124.

Ask a binary what it carries, without starting it:

$ coraine --version
coraine 0.4.0
features: SUBSCRIPTIONS=1 REGISTRATIONS=0 GEOQ=1 ...

and ask a running one for the whole picture with GET /build — the features compiled in, the plugins this build produced against the ones actually loaded, and the run-time settings that change what a client gets:

{
  "product": "coraine",
  "version": "0.4.0",
  "build":    { "gitSha": "...", "builtAt": "...", "type": "Debug", "compiler": "GNU 15.2.0" },
  "features": { "REGISTRATIONS": false, ... },
  "plugins":  { "directory": "/opt/seamware/plugins", "built": [...], "loaded": {...} },
  "runtime":  { "distributed": false, "splitEntities": true, "httpEndpoint": "..." }
}

Three kinds of fact, kept apart because they change at three different moments — a feature is fixed when the source was compiled, loaded is decided at startup from a directory the binary does not own, and runtime changes with a restart. That last one earns its place: a broker with REGISTRATIONS compiled in and --distributed off accepts registrations and forwards nothing, and this is the only place both switches are visible at once.

It is not under /admin on purpose — the admin API is itself a compile-time feature, and the endpoint that reports what a build contains must not be one of the things a build can leave out. GET /version is unchanged: product, version and the linked-library commits. "What am I talking to" and "what can it do" are different questions.

The functional suite asks the same question, of the --version line. A test that needs a feature carries # REQUIRE_FEATURE: <NAME> and leaves the run set on a build without it — 184 of the cases need REGISTRATIONS and 149 need SUBSCRIPTIONS — and # SKIP_FEATURE: <NAME> marks the ones that can only run on a build without it, which is how the 501s above are tested. Both markers take a list, and REQUIRE_FEATURE means ALL of them: a registration subscription needs REGISTRATIONS SUBSCRIPTIONS.

To build a reduced tree without turning your ordinary one into it:

make di CMAKE_FEATURES=-DCOR_FEATURE_REGISTRATIONS=OFF BUILD_DEBUG=BUILD_DEBUG_MINIMAL

⚠️ CMake caches what it is given, per build directory. Re-running that command with a different feature keeps the previous one off as well, which is easy to miss because the build succeeds — check coraine --version (or GET /build) rather than assuming. Give each combination its own directory.

⚠️ Building a stripped-down broker is at your own risk

Every COR_FEATURE_* option can be set to OFF and most of them will build. That is not the same as most of them working. Below is what each one actually does today, measured by building the broker fourteen times with one feature off at a time and comparing the result against a full build.

These two remove code from the broker and are covered by the test suite:

flag .text removed endpoints affected
COR_FEATURE_REGISTRATIONS=OFF 23,936 bytes 15 routes answer 501
COR_FEATURE_SUBSCRIPTIONS=OFF 18,880 bytes 7 routes answer 501

These two leave the broker unchanged and drop a plugin, which is the whole of their effect — the broker is a plugin loader and it simply has one fewer to load:

flag effect
COR_FEATURE_MONGOC=OFF mongoc.so is not built; run --database corDB
COR_FEATURE_ADMIN_API=OFF admin.so is not built

⚠️ These nine are declared and do nothing at all:

GEOQ, SCOPES, DATASETID, MULTI_TYPE, CONTEXT_DL, TENANTS, LOCATION, OBSERVATION_SPACE, OPERATION_SPACE

The option exists, CMake emits the -D, no source reads it, and the compiled binary is byte-for-byte the same as a full build. -DCOR_FEATURE_TENANTS=OFF produces a broker that builds, starts, reports "TENANTS": false on GET /build — and serves tenants exactly as before. This is the failure mode to watch for, because nothing about it looks like a failure. If you are switching one of these off to remove a capability, you have not removed it.

⚠️ These three do not build:

CONTEXT_HOSTING, METRICS, ICU_COLLATION

Their CMakeLists.txt drops the source files, and code that survives still references the symbols, so the link fails with undefined reference to 'getJsonldContexts', 'metricsPreService' and friends. A failed link is the honest outcome of the three — it is the nine above that will mislead you.

What to check. After any reduced build, ask the binary what it thinks it is rather than assuming the flag took:

$ coraine --version          # before it even starts
$ curl localhost:1026/build  # features, plugins built vs loaded, runtime

and remember that a false in that list means the flag was set, not the code is gone — for the nine above they are different statements.

If you hit trouble, please open an issue at github.com/SEAMWARE/coraine/issues saying which flags you set and what happened, and paste the GET /build output. Reduced builds are a direction this project is committed to, and the combinations nobody has tried are exactly the ones worth hearing about.

Choosing the HTTP server

cmake -DCOR_HTTP_SERVER=mhd        # libmicrohttpd (the default)
cmake -DCOR_HTTP_SERVER=builtin    # corHttp, no external HTTP dependency

Not a COR_FEATURE_* boolean, because those answer "is this capability in the build" and the HTTP server is always in it — what varies is which one. The value reaches CMake and corRest's own make (the HTTP server lives in corRest, which builds with plain make and knows nothing of CMake options), and it decides whether libmicrohttpd is on the link line at all, so ldd coraine is the check that it took. A running broker reports it as build.httpServer on GET /build.

builtin selects corHttp, a sibling repo: an HTTP/1.1 server on one edge-triggered epoll loop, depending on nothing but kalloc and libc. Requests still run on corRest's worker pool — the loop only does I/O — and the wire format it emits is byte-for-byte the one libmicrohttpd produced, because several hundred functional tests compare captured responses line by line.

Two things it does not do, and both are deliberate rather than pending:

  • No TLS server. The broker never serves HTTPS (it is put behind a proxy that does), so this costs exactly one thing: the test notification receiver ftClient cannot serve HTTPS either, and the one functional test that needs that carries REQUIRE_HTTPSERVER: mhd.
  • No HTTP pipelining. A second request arriving in the same packet as the first is dropped rather than answered. No client in use here pipelines — curl does not, and neither browsers nor the ETSI suite's HTTP library do — and supporting it properly means driving the event loop from the response side.

Both servers are tested, but not in the same place. A pull request builds and tests one of them — mhd, the default — because doubling a two-minute check to answer a question that changes once a month is the wrong trade. The nightly crosses its valgrind matrix and its ETSI job with COR_HTTP_SERVER, so builtin gets the whole functional suite under valgrind and the whole conformance suite, every night that something merged.

Why bother: libmicrohttpd is ~180 kB of mapped code, about 21% on top of the broker's own, for a library coraine uses 32 of the 81 exported symbols of — and it is the last third-party runtime dependency besides libc and OpenSSL once the optional features are compiled out. It is also a tarball fetched from ftp.gnu.org and built from source in the Dockerfile.

The optional runtime dependencies

The same holds for the optional runtime deps — MQTT notifications, for instance, are ~2 KB of broker code against a libmosquitto that every build links and every process maps, whether or not a single MQTT notification is ever sent.

Next

Once it builds, Testing covers running the suite and measuring coverage.