Cairn OBS is self-hosted, and the way to separate two environments is to run two installations rather than two tenants inside one. Tenancy is the wrong boundary for that, on three counts this repository demonstrates rather than assumes: chwriter.WriteBatch is all-or-nothing across tenants, so one tenant's failure stalls offset progress for every other; CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT puts every tenant's data behind a single superuser credential, as docker-compose.yml's own comment says; and one binary with one set of migrations moves every tenant together, which is the opposite of what separate environments are for. A whole installation idles at about 1.3 GB, so the sharing buys nothing. The project led with multi-tenant RBAC in the README banner and in PROJECT-SPEC's goal statement. Both now say what it is instead: self-hosted. "Open-core" goes with them -- it was already inaccurate, since CONTRIBUTING states there is no feature gate and no paid tier, and with enterprise/ off the roadmap there will not be one. A second identity provider comes off the list of things standing between this and production-ready. SSO belongs to enterprise/, and a self-hosted deployment is not waiting on it. Terraform's tenant/RBAC resources move from "disclosed future work" to not planned. Nothing is scrubbed from the record. Phase 4 stays shipped, its runbook stays, and its known gaps stay stated -- rewriting that history would contradict the candour the Status section is built on. enterprise/ stays in the tree, AGPLv3 and working, as the answer to a question this project is not asking. Signed-off-by: John Coffey <[email protected]>
6.8 KiB
Contributing to Cairn OBS
Thanks for your interest in contributing to Cairn OBS — self-hosted, Kubernetes-native log aggregation and observability. Contributions of all kinds are welcome: bug reports, feature requests, code, documentation, and testing.
Code of Conduct
By participating in this project you agree to abide by the Code of Conduct. Be constructive, be patient with newcomers, and keep discussion focused on the project.
Before You Start
- Read
docs/architecture.mdbefore changing any component. The storage/query split in particular is deliberate: ClickHouse serves analytics, Tantivy serves full-text, and one query language compiles to a single plan across both. Changes that blur that line need discussion first. - PostgreSQL is control-plane only — dashboards, panels, alert rules and state, notification targets, delivery log. Log data never touches it. That boundary is about transactional read-modify-write, not preference.
enterprise/is a separate Go module that core never imports from. Since the Phase 6 relicensing that boundary is architectural rather than legal — it keeps core buildable and deployable standalone, and keeps tenant resolution server-side. Please don't reach across it.- The whole repository is AGPLv3,
enterprise/included. There is no feature gate and no paid tier holding code back. Anything you contribute is distributed under AGPL-3.0, including for hosted/SaaS deployments.
How to Contribute
Reporting Bugs
Search existing issues first. When filing a bug, include:
- A clear, descriptive title
- Steps to reproduce, and expected vs. actual behaviour
- Which component —
agent,ingest,api,search,alerting,enterprise,web,cli,deploy - How you're running it:
docker compose, the Helm chart, or something else, and whichCOMPOSE_PROFILESif compose - Relevant logs from the component, and the query if the bug is in query behaviour
- Whether it reproduces from a clean
docker compose down -v && docker compose up
Two areas are known to be unverified rather than broken — Phase 4 (RBAC and tenant isolation) beyond its audit-logging guarantees, and the Windows agent, which has never run on real Windows. Both are called out in the README. Reports against them are welcome; please say that's what you were testing.
Suggesting Features
Open an issue describing:
- The problem you're trying to solve, not just the solution
- Where it sits relative to the architecture — which component owns it, and whether it crosses the storage/query split or the
enterprise/boundary - Roughly what it costs at volume, if it touches the ingest or query path. Cost-per-GB is a design goal, not an afterthought.
For larger changes, open an issue to discuss the approach before submitting a pull request — this saves everyone time if the direction needs adjusting.
Submitting Pull Requests
- Fork the repository and create your branch from
main. - Name your branch descriptively, e.g.
fix/tantivy-merge-stallorfeat/alert-absence-rules. - Keep PRs focused — one logical change per PR.
- Write clear commit messages describing what changed and why. The "why" matters more than the "what"; the diff already says what.
- Add or update tests in the component you touched. Each top-level directory carries its own unit tests.
- Update documentation if your change affects setup, configuration, the query language, or user-facing behaviour.
- Open the pull request against
main, with a summary, any related issue numbers, screenshots for UI changes, and what you actually ran to check it.
Please sign off your commits: git commit -s adds the Signed-off-by trailer, which is a statement that you have the right to submit the work under this project's licence. GitHub enforces it for commits made through the web interface; for everything else it is asked for rather than blocked.
CI
Three workflows run on a pull request:
- License compliance — the dependency licence inventory under
docs/compliance/must stay accurate. A new dependency with an incompatible licence fails the build. - Security scan
- Web route check — the route lists are kept honest against the actual SvelteKit routes, so an unrouted path answers 404 rather than rendering something.
Code Style
- Match the existing formatting and naming conventions in the component you're editing. It's a polyglot tree: Rust in
agentandsearch, Go iningest,api,alerting,enterpriseandcli, TypeScript and Svelte inweb. cargo fmtandgofmtoutput is the standard; don't hand-format around them.- Prefer clarity over cleverness. This is infrastructure people page on.
- Comment the non-obvious, especially anything about query planning, tenant scoping, or the exactly-once/at-least-once properties of the ingest path — those are easy to get subtly wrong and expensive to debug later.
Development Setup
- Clone your fork:
git clone https://github.com/YOUR-USERNAME/cairnobs.git cd cairnobs - Bring the stack up:
The web UI is on http://localhost:3000 and the API on
docker compose up:8080; alerting is on:8081and enterprise auth on:8082. The agent connects to ingest over mTLS gRPC on:4317.searchpublishes no host port — it's reachable only on the compose network. COMPOSE_PROFILESin.envselects the query-serving binary. Report againstsingle-tenantunless the bug is inenterprise/itself, which is off the roadmap (see the README):single-tenant(default) orenterprisefor the multi-tenant path. They're mutually exclusive, the same choice Helm'senterprise.enabledflag makes for a real cluster. Override per invocation withCOMPOSE_PROFILES=enterprise docker compose up.- Exercise the path you changed end to end — for ingest or query work that means getting a real log line in and querying it back, not just a passing unit test.
Review Process
- A maintainer will review your PR and may request changes.
- Please respond to review feedback in a timely manner; PRs with no activity for an extended period may be closed, and can be reopened once updated.
- Once approved, a maintainer will merge the PR.
Reporting Security Issues
Please do not open a public issue for security vulnerabilities. See SECURITY.md for how to report privately, and for what is in and out of scope — cross-tenant data exposure is the class we most want to hear about.
Questions?
If you're unsure whether something is a good fit, open an issue and ask, or start a discussion. Discussion before you invest time in a PR is welcome.
Thanks again for helping improve Cairn OBS.