OIBus Developer Handbook
Welcome to the OIBus developer community! This guide will help you get started with contributing to OIBus.
๐ Getting Startedโ
Prerequisitesโ
| Tool | Purpose | Required? | Installation |
|---|---|---|---|
| Git | Version control | โ | git-scm.com |
| Node.js | JavaScript runtime (see .nvmrc for version) | โ | nodejs.org (or install via nvm) |
| nvm | Node version manager | Recommended | nvm install guide |
| VS Code or IntelliJ | Code editor | Recommended | VS Code ยท IntelliJ |
| DBeaver or SQLite browser | Database inspection | Optional | DBeaver ยท SQLite browser |
๐ฅ Setting Up Your Development Environmentโ
1. Get the Source Codeโ
The OIBus source lives on GitHub at github.com/OptimistikSAS/OIBus. You'll use Git to clone it locally and to share your work back via pull requests.
Run GitHub's "Set up Git"
once to install and configure it. Add an
SSH key to use the
git@github.com:โฆ URLs below โ or swap them for https://github.com/โฆ and Git will prompt for a token
instead. The Pro Git book (free online) is the reference for everything
else.
Once Git is installed and authenticated, choose one of these options:
Option A: For Contributors (Recommended)
# 1. Fork the repository on GitHub (use the "Fork" button on the repo page)
# 2. Clone YOUR fork locally:
git clone git@github.com:<your-username>/OIBus.git
cd OIBus
# 3. Register the OptimistikSAS repo as a second remote called "upstream"
# so you can later pull in changes from the main project:
git remote add upstream git@github.com:OptimistikSAS/OIBus.git
# 4. Sanity-check both remotes are registered:
git remote -v
Option B: For Evaluation Only
git clone git@github.com:OptimistikSAS/OIBus.git
cd OIBus
Day-to-day Git workflowโ
A typical contribution looks like:
# Start from an up-to-date main
git checkout main
git pull upstream main
# Branch for your change โ see "Branch Naming" further down
git checkout -b feat/my-new-feature#1234
# Edit files, then stage and commit
git add <files>
git commit -m "feat(south): describe what you did"
# Push to YOUR fork (origin)
git push -u origin feat/my-new-feature#1234
# Open a Pull Request from your fork's branch into OptimistikSAS/OIBus:main
If main has moved while you were working, rebase to keep history linear:
git fetch upstream
git rebase upstream/main
# resolve any conflicts, then:
git push --force-with-lease
Use --force-with-lease (not plain --force) โ it refuses to overwrite remote commits you don't know
about, so you can't accidentally clobber a teammate's review fixup.
2. Install Dependenciesโ
# Install Node.js version specified in .nvmrc
nvm install
nvm use
3. Set Up the Backendโ
cd backend
npm install
npm start # Starts on http://localhost:2223
4. Set Up the Frontendโ
cd frontend
npm install
npm start # Builds and watches for changes
The frontend is served by the backend. While npm start watches for changes, you'll need to manually refresh your browser to see updates.
5. Set Up Documentationโ
cd documentation
npm install
npm start # Starts on http://localhost:3000
6. (Optional) Set Up the Launcherโ
Skip this unless you're working on the supervisor / auto-update path. The launcher is a standalone Node
package with its own dependencies and a separate Node version pin (node >= 24, see launcher/package.json).
cd launcher
npm install
npm test # runs node --test on src/**/*.spec.ts
npm run lint
# Bundle a binary for your platform (e.g. macOS arm64):
npm run build:macos-arm64
The bundled binary lands in build/bin/<platform>/oibus-launcher and that's what ships in the platform
installers โ it's responsible for starting the OIBus runtime binary as a child process.
7. Verify Your Setupโ
- Backend: http://localhost:2223
- Documentation: http://localhost:3000
๐ Development Workflowโ
Project Structureโ
OIBus/
โโโ backend/ # Backend server (Node.js + TypeScript)
โโโ frontend/ # Frontend application (Angular)
โโโ launcher/ # Process supervisor + auto-update (bundled to native binaries)
โโโ documentation/ # Project documentation (Docusaurus)
โโโ docker/ # docker-compose stack for simulating sources / destinations
โโโ data-folder/ # Runtime data (created automatically)
The launcher is a small TypeScript program that supervises the OIBus binary in production: it handles
the update/rollback cycle, manages PID files, and is the entry point shipped in the platform installers.
It's bundled via @yao-pkg/pkg into per-platform executables
(win-x64, macos-x64, macos-arm64, linux-x64, linux-arm64). You only need to touch it when you're
changing the supervisor itself, the on-disk layout it manages, or the update flow.
Quick Test Setupโ
To verify everything works:
- Create a FolderScanner South connector (reads files from a directory)
- Create a Console North connector (outputs to console)
- Configure them to work together
๐ง Development Guidelinesโ
Branch Namingโ
<type>/<descriptive-name>#<issue-number>
Examples:
feature/add-new-connector#1234fix/folder-scanner-bug#5678docs/update-readme#9101
Commit Messagesโ
Follow Conventional Commits:
<type>(<scope>): <description>
[optional body โ explain the *why*, not the *what*]
[optional footer โ e.g. "Closes #1234", "BREAKING CHANGE: ..."]
Common types: feat, fix, docs, style, refactor, perf, test, chore, build, ci.
Common scopes correspond to top-level concerns: south, north, engine, cache, transformer,
oianalytics, logger, web-server, migration, frontend, launcher, docs. Use the most specific
one that fits; omit the scope entirely if the change is cross-cutting.
Examplesโ
feat(south): add subscription support to south-rest
Extends the REST connector to maintain a long-lived SSE connection and push
events to the engine via addContent. Falls back to polling if the server
responds with anything other than text/event-stream on the subscribe call.
Closes #4231
fix(cache): write the correct chunk content when maxNumberOfElements > 0
cacheWithoutTransform was JSON.stringify-ing the full content array for each
chunk file instead of the chunk itself, duplicating every payload.
perf(north): fan out cacheContent in parallel across enabled Norths
Replaces the sequential await loop in data-stream-engine.addContent with
Promise.all + per-North .catch. A slow North no longer blocks healthy ones.
refactor(south-opcua): cache item-per-node lookup in HistoryRead loop
Replaces the O(Nยฒ) Array.find per response result with an item bundled into
the nodesToRead array, accessed by index.
docs(create-connector): rewrite outdated class and manifest pages
chore(deps): bump better-sqlite3 to 12.9.0
feat(north)!: replace handleValues/handleFile with handleContent
BREAKING CHANGE: North subclasses must now implement a single
handleContent(fileStream, metadata) method and declare supportedTypes()
instead of canHandleValues / canHandleFiles flags.
The squash-merge into main uses the PR title as the commit message, so make sure the title follows
this format โ the individual commits on your branch can be looser.
Testing Requirementsโ
All changes must include tests:
- Backend: Node's built-in
node:testrunner, with TypeScript transpiled on the fly viatsx. Coverage is collected automatically when runningnpm test(seetestRunnerinbackend/package.jsonfor the exclude list). - Frontend: Jasmine via the Angular test harness.
- Launcher: Same
node:testrunner as the backend.
Run tests with:
# Backend โ runs all specs with coverage
cd backend
npm test
# Frontend tests
cd frontend
npm test
# Launcher tests (only if you touched launcher/)
cd launcher
npm test
# Linting (run in each package you touched)
npm run lint
๐ค Submitting Contributionsโ
Before You Startโ
- Check existing issues for similar work
- Create a feature issue if adding new functionality
- Discuss your approach with maintainers before coding
Pull Request Processโ
- Create a branch from
mainwith proper naming. - Make your changes and commit with clear messages.
- Run the checks locally โ before pushing, confirm each box:
- Code follows the project style (
npm run lintin every package you touched). - All tests pass (
npm test). - New features include tests; existing coverage is maintained.
- Documentation updated if the change is user-visible or alters the developer workflow.
- Changes are backward compatible (or the breaking change is called out in the PR title with
!and in the body with aBREAKING CHANGE:footer).
- Code follows the project style (
- Push to your fork and open a Pull Request to
OptimistikSAS/OIBus:main. - Wait for code review and address feedback. Use
--force-with-leasefor any rebase pushes (see the Git workflow).
๐ค Community Guidelinesโ
How to Contributeโ
- Start small: Fix typos, improve docs, or tackle "good first issue" labels
- Ask questions: Use GitHub discussions or issues
- Be patient: We'll review your PR as soon as possible
- Stay engaged: Be responsive to feedback
Code of Conductโ
We follow a Code of Conduct to ensure a welcoming community.
๐ Learning Resourcesโ
Technologies Usedโ
| Area | Technology | Learning Resources |
|---|---|---|
| Backend | Node.js | Node.js Docs |
| Frontend | Angular | Angular Docs |
| Documentation | Docusaurus | Docusaurus Docs |
| Testing | node:test / Jasmine | node:test Docs, Jasmine Docs |
Recommended Readingโ
Deep-links into the official documentation of the libraries OIBus relies on most. Each one targets the section that's directly useful when working on the codebase โ not generic landing pages.
- TypeScript Handbook โ the backend and frontend are both strict TypeScript; the Handbook's chapters on generics and discriminated unions are particularly relevant to the manifest / settings layer.
- Angular guide โ the frontend is built with Angular, including reactive forms (used to render manifests) and standalone components.
- Node.js
node:streammodule โ the cache pipeline, transformers, and NorthhandleContentwork with streams; understanding backpressure andpipeline()matters. - Pino logger โ every connector uses
this.logger; the log-levels and child-logger patterns inform the conventions documented in the connector class guide. - better-sqlite3 โ the local cache (entities, metrics, logs, south-cache) is all better-sqlite3; prepared statements and transactions show up in every repository.
- Knex migrations โ schema changes go through Knex; the
migration files under
backend/src/migration/follow this guide's conventions. - Node.js
node:testโ the test runner used by both the backend and the launcher. Key sections: mocking, mock timers, and coverage (both are used heavily in connector specs). - Docusaurus โ for editing the documentation site itself.
- Conventional Commits and Semantic Versioning โ the commit-message format and release versioning conventions OIBus uses.
๐ฏ First Contributionsโ
A few concrete starting points, ranked from lowest to highest investment. Pick whichever matches your available time and familiarity with the stack.
Documentation fixesโ
The fastest way to make your first PR land. Most pages live in documentation/docs/ as Markdown / MDX โ
typos, missing examples, outdated screenshots, and broken links are all valuable contributions. Run
npm start in documentation/ to preview your changes locally.
Search open documentation issues, or just fix something you noticed while reading these docs.
Bug fixesโ
Look for issues tagged good first issue
โ those are intentionally scoped so a new contributor can get to a working PR without needing a guided
tour of the codebase. If nothing matches your interest, scan the broader
bug label and pick a reproducible one. Comment on
the issue before you start so we can confirm nobody else is already on it and answer any preliminary
questions.
Improve an existing connectorโ
OIBus's South / North connectors are a great way to learn the codebase incrementally: each one is a small
self-contained class with a manifest and a spec, and they all follow the same pattern. Useful entry-level
work includes adding a settings option, improving error messages, or extending a testConnection() to
surface more diagnostics.
Start from backend/src/south/south-<type>/ (or the equivalent North folder) and skim the corresponding
.spec.ts to see what's covered. Every PR should keep coverage at 100%.
Add a new connectorโ
For larger contributions, see the dedicated guide: Create a new OIBus connector. It walks through the file layout, the four registration steps (type list, factory, type-generator, translations), the manifest format, and the connector class API with full working examples.
Benchmark the productโ
We welcome contributions that measure OIBus's behaviour under realistic load and either propose a
targeted fix or simply document the finding. The repository ships a docker-compose.yml with simulated
sources and destinations specifically to make this kind of work approachable:
| Container | Profile | Purpose |
|---|---|---|
opcua-server | (default) | Microsoft opc-plc โ OPC UA server with 8 configurable nodes and historian support |
modbus-server | (default) | oitc/modbus-server โ Modbus TCP server |
simulator | (default) | Python script writing sinusoidal values to both Modbus and MQTT |
mqtt-broker | (default) | Eclipse Mosquitto broker (authenticated, WebSocket on port 9001) |
postgres | (default) | PostgreSQL for South-PostgreSQL |
ftp-server / sftp-server | testing | File-based sources |
oibus / nginx | oibus | Full OIBus runtime + reverse proxy for end-to-end testing |
Bring the stack up with docker compose up -d (some services live behind the testing / oibus Docker
Compose profiles โ see the file for details). Then configure OIBus to point at the simulators and observe
behaviour as you ramp item counts, scan-mode frequency, or batch sizes.
Full details on every service, its image, the simulated signals, and configuration options are in the Local Test Stack page.
What's most useful to measure:
- Throughput under sustained load โ items/sec a South can ingest without queue backpressure; files/sec a North can deliver without piling up in the error folder.
- Memory growth โ heap and RSS over a multi-hour run, looking for slow leaks in transformer or cache paths.
- Boot time โ startup time after a few million logs / a backlogged cache. Improvements here are visible to every operator on every restart.
- DB hot paths โ the SQLite caches (
south-cache.repository,log.repository, the metrics repositories) all sit on writer threads; a profiler trace pointing at one specific query is gold.
When proposing a fix, include a before/after measurement in the PR description (even a rough one โ a
process.hrtime.bigint()-wrapped microbenchmark or a screenshot of a running OIBus is better than no
number at all). That makes review faster and gives the team a baseline for regression checks later.
Ready to contribute? We're excited to have you! ๐