Skip to main content

OIBus Developer Handbook

Welcome to the OIBus developer community! This guide will help you get started with contributing to OIBus.

๐Ÿš€ Getting Startedโ€‹

Prerequisitesโ€‹

ToolPurposeRequired?Installation
GitVersion controlโœ…git-scm.com
Node.jsJavaScript runtime (see .nvmrc for version)โœ…nodejs.org (or install via nvm)
nvmNode version managerRecommendednvm install guide
VS Code or IntelliJCode editorRecommendedVS Code ยท IntelliJ
DBeaver or SQLite browserDatabase inspectionOptionalDBeaver ยท 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.

New to Git?

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
Frontend Note

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โ€‹

๐Ÿ›  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:

  1. Create a FolderScanner South connector (reads files from a directory)
  2. Create a Console North connector (outputs to console)
  3. Configure them to work together

๐Ÿ”ง Development Guidelinesโ€‹

Branch Namingโ€‹

<type>/<descriptive-name>#<issue-number>

Examples:

  • feature/add-new-connector#1234
  • fix/folder-scanner-bug#5678
  • docs/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โ€‹

Feature commit
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
Bug fix
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.
Performance
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
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 / chore
docs(create-connector): rewrite outdated class and manifest pages
chore(deps): bump better-sqlite3 to 12.9.0
Breaking change
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:test runner, with TypeScript transpiled on the fly via tsx. Coverage is collected automatically when running npm test (see testRunner in backend/package.json for the exclude list).
  • Frontend: Jasmine via the Angular test harness.
  • Launcher: Same node:test runner 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โ€‹

  1. Check existing issues for similar work
  2. Create a feature issue if adding new functionality
  3. Discuss your approach with maintainers before coding

Pull Request Processโ€‹

  1. Create a branch from main with proper naming.
  2. Make your changes and commit with clear messages.
  3. Run the checks locally โ€” before pushing, confirm each box:
    • Code follows the project style (npm run lint in 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 a BREAKING CHANGE: footer).
  4. Push to your fork and open a Pull Request to OptimistikSAS/OIBus:main.
  5. Wait for code review and address feedback. Use --force-with-lease for any rebase pushes (see the Git workflow).

๐Ÿค Community Guidelinesโ€‹

How to Contributeโ€‹

  1. Start small: Fix typos, improve docs, or tackle "good first issue" labels
  2. Ask questions: Use GitHub discussions or issues
  3. Be patient: We'll review your PR as soon as possible
  4. Stay engaged: Be responsive to feedback

Code of Conductโ€‹

We follow a Code of Conduct to ensure a welcoming community.

๐Ÿ“š Learning Resourcesโ€‹

Technologies Usedโ€‹

AreaTechnologyLearning Resources
BackendNode.jsNode.js Docs
FrontendAngularAngular Docs
DocumentationDocusaurusDocusaurus Docs
Testingnode:test / Jasminenode:test Docs, Jasmine Docs

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:stream module โ€” the cache pipeline, transformers, and North handleContent work with streams; understanding backpressure and pipeline() 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:

ContainerProfilePurpose
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-servertestingFile-based sources
oibus / nginxoibusFull 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! ๐ŸŽ‰