The design argues the conformance suite is the specification and should
be built before either implementation, since two hand-written
implementations of one language diverge unless something shared pins
them. This is that suite: 38 cases in /processing, a language-neutral
top-level directory for the same reason /proto is one -- the Rust agent
and the Go ingest tier both consume the definition and neither owns it.
Nothing executes the cases, because neither implementation exists. A
stdlib-only validator checks the corpus stays well-formed and runs in
CI: known actions, addressable fields, compilable patterns, names
matching filenames, and no case depending on record_id, which is
withheld so the question of whether an ingest-side rule can see one
stays open. The validator was checked against seven deliberately broken
cases before being trusted, since "38/38 valid" means nothing from a
validator that cannot fail.
Writing the cases first has already paid for itself twice.
It forced two determinism decisions the prose had left vague, both of
which a conformance suite cannot avoid answering. Sampling is
counter-based rather than random: random is statistically nicer and
impossible to assert on. Windows are measured on record timestamps
rather than wall-clock, which makes replay deterministic and, not
incidentally, makes backfill behave correctly where a wall-clock window
would not.
And it made the missing aggregate_count answer concrete. The design
does not say what that action emits, or what a query not expecting a
synthetic record sees. Rather than invent one by writing cases, the
validator rejects any case using it, so the design question has to be
answered before the behaviour can be frozen by accident.
The corpus includes the acceptance case from real measured data: the
two processes that account for roughly 60% of a real workstation's
journal volume, and the one kernel message worth keeping.
Signed-off-by: John Coffey <[email protected]>
Three of the roadmap's claims were contradicted by the repository
itself.
Fleet management was Phase 11, "Planned", and positioning.md said config
still flowed to the agent from the host rather than from the platform.
agent-management-design.md has recorded the opposite for some time:
punch list complete, verified live, with central authoring, versioning,
rollout on the next check-in, observation, and a restart command a real
agent picks up and acts on. There is no Phase 11 now. Its remainder is
either already named there -- stop/uninstall, per-host multi-row
alerting, a rule-per-host generator -- or belongs to Phase 8, since
distributing rules is the one genuinely new thing the mechanism has to
carry, and a rule language nobody can push to a fleet is not worth
having.
That inverts the old ordering argument, which put fleet last on the
grounds that it manages configuration the earlier phases define. Sound
reasoning; the world went the other way and built the mechanism first.
Recorded rather than quietly dropped, because the instinct behind it is
a good one that happened not to apply.
Retention was listed as a question Phase 10 would finally have to
answer. Half of it is answered: api/logretention serves operator-driven
preview and delete with an owner-only per-agent floor. What is missing
is an automatic TTL, so Phase 10 owns tiering and automatic TTL rather
than retention from nothing.
The rule-language recommendation is now settled rather than proposed,
and not on its own authority: DesiredOverride is already a closed typed
shape that cannot carry arbitrary code, so no channel exists that would
deliver JavaScript to an agent even if the language argument had gone
the other way.
That surfaced a requirement nothing had written down. Agent management
rests on an invariant it states outright -- every editable field
degrades behaviour without cutting off the agent's ability to receive
the next correction. Processing rules break it: a rule that panics or
loops strands the agent exactly the way a corrupted ingest endpoint
would, across every host it reached first. Phase 8 now owes either total
evaluation or apply-then-verify with rollback, chosen deliberately
rather than discovered mid-rollout.
Signed-off-by: John Coffey <[email protected]>
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]>
Local login is implemented, wired through api, alerting and web, and
undiscoverable. No compose file turns it on, the Helm chart sets none
of its variables, and no markdown in the repository mentions
-seed-admin, LOCAL_AUTH_ENABLED or local login at all. The only way to
find it is to read cmd/api/main.go's authorizer switch.
Enabling it in docker-compose.yml is not the answer: a plain
`docker compose up` has no authentication, and every Phase 0-3 runbook
verifies the pipeline with bare curl against /query. Turning login on
by default would break the project's own documented verification.
So it's an opt-in overlay instead.
Four settings have to agree, and only one of them is obviously about
login. Each fails differently and none of the failures name the cause:
the route 404s, or the browser refuses the request before sending it,
or login returns 200 and every later request is anonymous because the
cookie was never stored, or the same symptom again from the opposite
end because the bundle never attaches it. That is what the new document
is mostly for.
The Helm chart still has no local-login support. Recorded in the
document as a gap rather than papered over.
Signed-off-by: John Coffey <[email protected]>
The Status section went from a table of "Shipped" straight into three
paragraphs of caveats, with nothing in between telling a reader what
standard was being applied. Read cold, that is a project confessing. Read
with the standard stated first, it is a project that refuses to call
something done because the tests pass.
So the section now opens by saying what stage this is -- pre-1.0, no
production workload -- and what "shipped" means here: verified against
real infrastructure with a runbook recording how, including what the
verification could not reach. And it says plainly why the caveats are
long, which is that they are disclosed rather than discovered. A shorter
Status section would not mean a more finished product, only a less
careful one.
It closes with what would actually close the gap: a second IdP, a real
cluster, the Windows agent on Windows, sustained load, and somebody
else's data. None of that is research, it is time on real infrastructure
-- which makes it the list a pilot works through, and makes a 1.0 tag the
wrong next milestone to reach for.
Written because the risk of a public repo at this stage is not a
competitor reading the roadmap, it is a prospect reading unusual honesty
as immaturity. The fix for that is context, not privacy.
Splunk and Cribl are not the same competitor and the claim is not the
same claim twice. Splunk is the destination and Cairn OBS replaces it,
which is what phases 0-7 were for. Cribl is the road: routing, reduction,
enrichment, redaction and replay on the way to wherever data is going.
Cairn OBS is already a road in shape -- agent, Redpanda, ingest -- and
exposes none of a pipeline's controls. The agent cannot filter, drop,
sample, mask or re-route anything; ingest normalises a schema and writes
it; there is exactly one destination and it is us.
Reconciling the two turns up something a cost-led project has to face
rather than paper over: most people buy Cribl because Splunk is expensive
per gigabyte, so being genuinely cheap per gigabyte removes the main
reason to buy Cribl in front of us. That makes the strongest pitch "one
system where there were two" rather than "we are also a pipeline vendor"
-- but that pitch only survives a buyer if we also do the four things
people buy a pipeline for that are not about spend: routing to several
destinations, redacting before data leaves the network, archive and
replay, and not being locked to one analytics vendor. Those are about
control, which is better ground anyway: cost advantages get matched and
architectural ones do not.
The consequence is uncomfortable and is written down as a decision rather
than left to be discovered: competing with Cribl means being able to send
data to S3, Splunk HEC, Elastic, OTLP and Kafka -- building features whose
purpose is to help data leave this platform. A project that refuses
lock-in in its licence and then builds it into its egress would be lying
about itself.
Four phases follow, ordered so each pays for itself: processing (8),
routing (9), archive and replay (10), fleet (11). Processing without
routing still shrinks what is stored; routing without processing forwards
everything and helps nobody.
One design decision is called out now because it collides with a
non-negotiable constraint. Cribl's rule language is JavaScript, and
embedding a JS engine in a statically-linked musl agent would end "no
glibc runtime deps" as a claim. The recommendation is a declarative rule
DSL -- matchers and typed actions, no arbitrary code -- deliberately less
expressive, small enough to audit and safe to push to ten thousand hosts.
The retention/TTL question in architecture.md is no longer deferrable and
now says so: Phase 10 asks it from the other side.
The status file and the README both still said Phase 4 was not shipped
because the environment had lost Docker and database access partway
through, and that only the audit-logging guarantees had been confirmed
against a live database. That stopped being true some time ago.
phase-4-runbook.md records the opposite in detail: Docker access came
back, a real docker-compose stack ran with real ClickHouse and Postgres
and two provisioned tenants, a local kind cluster ran the Helm chart end
to end, and both SSO protocols were verified against a real Auth0 tenant
with full browser round trips. Eight real bugs came out of that, six from
compose and two from the chart's first real install -- none of them
findable without the infrastructure.
PROJECT-SPEC.md sends readers to status.md and tells them to read it
before assuming a capability works end to end, so the one file that is
meant to be authoritative was the one understating the project by the
widest margin.
Correcting it matters more now than it would have last week, because the
evidence cannot be regenerated: proto.cairnobs.org and the VPS under it
were retired on 2026-09-04, taking the mTLS CA, the server certificate
and six enrolled agents with them. The runbooks are what is left.
Three gaps are now stated rather than implied:
The prototype is gone, so none of this can be re-run today without
building one. The DNS was kept for that; the certificates deliberately
were not.
demo.cairnobs.org is live and is not evidence for Phase 4. It runs
COMPOSE_PROFILES=single-tenant, so it exercises the OSS path and says
nothing about RBAC, tenant isolation or per-tenant ClickHouse. A healthy
demo proving multi-tenancy is exactly the wrong inference to leave
available.
SSO has been tried against one IdP and one local kind cluster, not two
IdPs and not a production-grade cluster.
The Terraform entry now names its cause instead of pointing at another
file: alerting exposes no PUT for rules or targets, and neither
rulestore.Store nor notifystore.Store has an Update method to wire one
to, so Terraform destroys and recreates -- which resets alert_state and
delivery-log continuity.
Unlike the other four projects, cairnobs had no copyright statement to change.
The Licensing section named the licence and pointed at LICENSE, but never said
who holds the copyright, and there were no SPDX headers in the source either.
That is a gap rather than a style choice: an AGPL project with no stated holder
leaves the question open, and the licence's obligations are written around
there being one. So this ADDS a line rather than replacing one.
The two LINUXexpert-org references here are github.com issue and discussion
links in CONTRIBUTING.md -- the repository's real path, unchanged.
LICENSE untouched. Its only copyright is the FSF's on the AGPL text.
The horizontal logo already carries the wordmark, so "# Cairn OBS"
directly beneath it repeated the same words twice in the first inch of
the page. Removed, and the tagline and licence line centred under the
logo so the header reads as one block.
The bold and the link are written as <strong>/<a> rather than markdown:
GitHub does not process markdown inside a raw HTML block, so **...** and
[...](...) would have rendered as literal asterisks and brackets.
Nothing linked to the removed heading, and the #licensing anchor it
referenced comes from `## Licensing`, which is untouched.
Uses the brand assets already in the repo rather than adding a copy --
web/src/lib/assets/logo-horizontal-{light,dark}.svg are the same files
NavSidebar renders, so the README cannot drift from the app's logo.
Wrapped in <picture> with a prefers-color-scheme source, since the two
variants differ by the colour they are drawn *for*, not by name: -light
carries #111315 text for light backgrounds, -dark carries #F2F2F2 for
dark ones. A single file would have been near-invisible on one of
GitHub's two themes. Both have transparent backgrounds -- the only
<rect> in each is a small design element, not a fill -- so neither
variant shows a card behind it.
CLAUDE.md was doing two jobs: durable repo conventions, and a ~500-line
phase-by-phase status narrative that duplicates the per-phase runbooks
and goes stale the moment a phase ships.
Keep mission, constraints, pinned stack, conventions, and "when in doubt"
in CLAUDE.md (572 -> 78 lines). Move the phase record verbatim to
docs/status.md, prefaced with a summary table and the known verification
gaps. Content is byte-identical; nothing was reworded or dropped.
Link both directions, and point the README's status section at the new
file.
Overview, architecture, layout, local-dev quickstart, and phase status
for Cairn OBS. Records the Phase 4 verification gap and the unverified
Windows agent path rather than presenting them as shipped.