Rebrand: Sentry -> Cairn OBS
Full rebrand across cosmetic branding, code identifiers, and infrastructure/data-plane naming, using the supplied Cairn OBS logo package. Cosmetic: favicon/logo swap (also closes a stale license-audit finding -- the old favicon was SvelteKit's unreplaced scaffold logo), new centered welcome landing page, larger/legible sidebar logo, page titles, CLAUDE.md/README/docs prose. Code identifiers: Go module path github.com/sentry/sentry -> github.com/cairnobs/cairnobs across all 13 modules and ~91 files (protoc regenerated); Rust crates sentry-agent/sentry-parser/sentry-search -> cairnobs-*; CLI sentryctl -> cairnobsctl; Terraform provider fully renamed (sentry_dashboard etc. -> cairnobs_dashboard, provider type, env vars); every session/auth cookie name; agent config paths and Windows service identity. Deliberately preserved: the gRPC wire protocol's protobuf packages (sentry.logs.v1, sentry.agent.v1) and their Go import directory (proto/sentry/...) -- renaming the wire-level package would break every currently-deployed agent binary (confirmed two real hosts, including mail.inbuxa.com, are actively streaming through this exact contract) until rebuilt and redeployed in lockstep with an ingest cutover. Only the Go module path wrapping the generated code changes. Infrastructure: every docker-compose container name (root and three component-level compose files); the Helm chart (directory, Chart.yaml, named-template helpers, all templates, values.yaml image repos); Kubernetes Operator (CRD group sentry.io -> cairnobs.io, both CRD YAML files, Go identifiers, RBAC markers); the coupled enterprise/tenantcrd package. Caught and fixed real path-coupling bugs along the way: the Helm chart's search/ingest volume mounts and the dev-only-credential detection constant vs. docker-compose.yml's literal values had to move together or a security warning would have silently stopped firing. Data plane: Postgres database sentry_metadata -> cairnobs_metadata and role sentry -> cairnobs; ClickHouse database sentry -> cairnobs; Kafka topic sentry.logs.raw -> cairnobs.logs.raw and its consumer groups. Source-level defaults, docker-compose.yml, and every migrate.sh/ provision script default updated together; already-applied migration files left untouched per this repo's immutable-migration convention. Verified at every layer: all 13 Go modules build/vet/test clean, both Rust workspaces (agent, search) build/clippy/test clean, npm run check/ build clean, docker compose config validates on all four compose files. Live-verified against a real docker stack multiple times through this work, including a final fresh-volume run confirming the actual renamed Postgres database/role, ClickHouse database, and Kafka topic all work end to end with a real login and query, zero console errors.
This commit is contained in:
@@ -10,7 +10,7 @@ reachable, and how quickly the platform notices when it stops.
|
||||
|
||||
The agent's transport has always been push-only, by design — it dials
|
||||
*out* to `ingest` over mTLS; nothing in the platform ever dials into an
|
||||
agent (see `agent/sentry-agent/src/grpc.rs`'s doc comment). Making
|
||||
agent (see `agent/cairnobs-agent/src/grpc.rs`'s doc comment). Making
|
||||
liveness detection a true pull (the platform reaching into every remote
|
||||
host on a schedule) would mean every agent needs a reachable address and
|
||||
an open inbound port — a real problem for hosts behind NAT or with
|
||||
@@ -28,7 +28,7 @@ model this reuses unchanged.
|
||||
|
||||
## Configuring the heartbeat
|
||||
|
||||
`agent/sentry-agent/config/agent.example.toml`:
|
||||
`agent/cairnobs-agent/config/agent.example.toml`:
|
||||
|
||||
```toml
|
||||
[heartbeat]
|
||||
@@ -40,7 +40,7 @@ The heartbeat record is sent through the exact same `PushBatch` RPC and
|
||||
mTLS identity every log line uses, bypassing the batch buffer (`[batch]`
|
||||
`max_size`/`flush_interval_ms`) so it's punctual rather than subject to
|
||||
batching delay. It's distinguished from real log data purely by an
|
||||
attribute — `sentry.heartbeat=true` — not by a fake `service` value, so
|
||||
attribute — `cairnobs.heartbeat=true` — not by a fake `service` value, so
|
||||
it never pollutes service-based dashboards or faceting. `message` is the
|
||||
literal string `"agent heartbeat"`.
|
||||
|
||||
@@ -55,7 +55,7 @@ heartbeat doesn't look like a false absence):
|
||||
curl -X POST http://localhost:8081/rules -H 'Content-Type: application/json' -d '{
|
||||
"name": "web-01 unavailable",
|
||||
"description": "fires when web-01 misses its heartbeat window",
|
||||
"query": "earliest=-3m host=web-01 sentry.heartbeat=true",
|
||||
"query": "earliest=-3m host=web-01 cairnobs.heartbeat=true",
|
||||
"query_language": "spl",
|
||||
"condition_type": "absence",
|
||||
"eval_interval_seconds": 60,
|
||||
@@ -123,7 +123,7 @@ this was simply never exercised in this specific way before) and a
|
||||
curl -X POST http://localhost:8081/rules -H "Content-Type: application/json" -d "{
|
||||
\"name\": \"fleet degraded\",
|
||||
\"description\": \"fires when fewer than 3 of the expected fleet hosts have heartbeated recently\",
|
||||
\"query\": \"SELECT count(DISTINCT host) AS active_agents FROM logs WHERE timestamp > now() - INTERVAL 3 MINUTE AND attributes['sentry.heartbeat'] = 'true' AND host LIKE 'web-%'\",
|
||||
\"query\": \"SELECT count(DISTINCT host) AS active_agents FROM logs WHERE timestamp > now() - INTERVAL 3 MINUTE AND attributes['cairnobs.heartbeat'] = 'true' AND host LIKE 'web-%'\",
|
||||
\"query_language\": \"sql\",
|
||||
\"condition_type\": \"threshold\",
|
||||
\"comparator\": \"lt\",
|
||||
@@ -172,8 +172,8 @@ wanted, not duplicated as a one-off script now.
|
||||
## Verified live
|
||||
|
||||
This exact flow was run end-to-end against a live stack in this repo: a
|
||||
real `sentry-agent` binary, heartbeat interval 5s, connected to a real
|
||||
`ingest`; an absence rule (`earliest=-45s host=... sentry.heartbeat=true`,
|
||||
real `cairnobs-agent` binary, heartbeat interval 5s, connected to a real
|
||||
`ingest`; an absence rule (`earliest=-45s host=... cairnobs.heartbeat=true`,
|
||||
`eval_interval_seconds=30`, `for_minutes=0`) created via the REST API
|
||||
above; the agent process killed; the rule transitioned `ok` → `firing`
|
||||
within one evaluation cycle (`condition_true_since`/`fired_at` both set
|
||||
|
||||
@@ -44,7 +44,7 @@ New proto (`proto/sentry/agent/v1/agent_control.proto`), a second gRPC
|
||||
service on the exact same mTLS channel/listener `LogIngest.PushBatch`
|
||||
already uses — not a second protocol or connection the agent has to
|
||||
maintain. Called on the agent's own heartbeat ticker (see
|
||||
`agent/sentry-agent/src/main.rs`'s `heartbeat_ticker` arm),
|
||||
`agent/cairnobs-agent/src/main.rs`'s `heartbeat_ticker` arm),
|
||||
independently of whether the heartbeat log record itself is enabled —
|
||||
CheckIn keeps running even with `heartbeat.enabled = false`, since
|
||||
that's an agent's only path to ever receive a remote override that
|
||||
@@ -98,7 +98,7 @@ heartbeat constantly.
|
||||
|
||||
The agent's local `agent.toml` is never rewritten. A remote override
|
||||
lives only in the running process's memory
|
||||
(`agent/sentry-agent/src/main.rs`'s `apply_override`) and is re-applied
|
||||
(`agent/cairnobs-agent/src/main.rs`'s `apply_override`) and is re-applied
|
||||
fresh on every check-in that returns one — a restarted agent boots from
|
||||
`agent.toml` alone and re-syncs whatever override is still set on its
|
||||
next successful check-in. This was a deliberate simplicity choice over
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sentry Architecture
|
||||
# Cairn OBS Architecture
|
||||
|
||||
> **Status:** Updated through Phase 4. The component map/diagram below is
|
||||
> still the Phase 0 request path (agent → ingest → ClickHouse → api →
|
||||
|
||||
@@ -28,7 +28,7 @@ like BSL/SSPL, anything with a field-of-use or non-compete restriction).
|
||||
|
||||
1. **Inventory** every dependency, direct and transitive, in every
|
||||
language ecosystem present in the repo:
|
||||
- **Rust** (`agent` workspace: `sentry-agent`, `sentry-parser`;
|
||||
- **Rust** (`agent` workspace: `cairnobs-agent`, `cairnobs-parser`;
|
||||
`search`): `cargo-deny` (`cargo deny list --format tsv`), installed
|
||||
fresh for this audit (`cargo install cargo-deny --locked`).
|
||||
- **Go** (`api`, `ingest`, `alerting`, `enterprise`,
|
||||
@@ -191,18 +191,18 @@ Key facts, verified against primary sources:
|
||||
plaintext core Kafka-protocol functionality — no RCL-gated enterprise
|
||||
features, no tiered storage, no SASL/RBAC — squarely within the
|
||||
permitted grant as an internal transport layer.
|
||||
- **No AGPL linking/compatibility issue.** Sentry never links against
|
||||
- **No AGPL linking/compatibility issue.** Cairn OBS never links against
|
||||
Redpanda's code; it's consumed purely over the Kafka wire protocol, the
|
||||
same relationship as ClickHouse and Postgres. AGPLv3's copyleft
|
||||
doesn't reach across a network-protocol boundary to unrelated,
|
||||
separately-licensed software you merely talk to.
|
||||
- **The genuinely open question**: Phase 6 relicenses `enterprise/` to
|
||||
AGPLv3 specifically so that anyone, including competitors, can legally
|
||||
self-host or fork Sentry — including offering it as a network service,
|
||||
self-host or fork Cairn OBS — including offering it as a network service,
|
||||
per AGPLv3's own terms. If a third party does that using the bundled
|
||||
`docker-compose.yml` (which pulls this BSL-licensed Redpanda image),
|
||||
does *their* deployment trip BSL's Streaming-or-Queuing-Service
|
||||
restriction? Sentry's ingest pipeline creates fixed internal topics,
|
||||
restriction? Cairn OBS's ingest pipeline creates fixed internal topics,
|
||||
not per-end-user topics exposed for direct third-party production or
|
||||
consumption — so this is very likely **not** a Streaming-or-Queuing-Service
|
||||
under BSL's own definition. But this is a business/redistribution
|
||||
@@ -211,7 +211,7 @@ Key facts, verified against primary sources:
|
||||
|
||||
### Remediation options (recorded per task 4's requirement)
|
||||
|
||||
1. **Accept as-is.** Document the reasoning above; Sentry's own use is
|
||||
1. **Accept as-is.** Document the reasoning above; Cairn OBS's own use is
|
||||
clearly within BSL's permitted grant, and the third-party-SaaS
|
||||
scenario is a reasonable-but-unverified reading, not a known
|
||||
violation. Lowest effort, zero functional change.
|
||||
@@ -241,22 +241,23 @@ deployment change was made as a result — Redpanda stays pinned at
|
||||
v24.2.7 in `docker-compose.yml`/`transport/`, under BSL 1.1, as a
|
||||
disclosed and accepted risk rather than an unresolved one. This
|
||||
decision should be revisited if the project's redistribution posture
|
||||
changes materially (e.g. an official hosted/managed offering of Sentry
|
||||
itself, which would make the third-party-SaaS reading in this section
|
||||
Sentry's *own* situation rather than a hypothetical third party's).
|
||||
changes materially (e.g. an official hosted/managed offering of Cairn
|
||||
OBS itself, which would make the third-party-SaaS reading in this section
|
||||
Cairn OBS's *own* situation rather than a hypothetical third party's).
|
||||
|
||||
## Non-license finding: `favicon.svg`
|
||||
|
||||
`web/src/lib/assets/favicon.svg` is SvelteKit's own default project
|
||||
`web/src/lib/assets/favicon.svg` was SvelteKit's own default project
|
||||
scaffold logo (`<title>svelte-logo</title>` — the `sv create`/`create-svelte`
|
||||
starter icon), never replaced with an original mark during Phase 5's
|
||||
redesign. Not a license-compatibility blocker — Svelte's own project
|
||||
assets are MIT-licensed — but it's unauthored, third-party-branded
|
||||
assets are MIT-licensed — but it was unauthored, third-party-branded
|
||||
content shipping as this product's own favicon, caught by the same
|
||||
"grep for anything that looks copied" pass this audit's task 1 asked
|
||||
for. Recorded as an action item (replace with an original Sentry mark),
|
||||
not a compliance blocker; not fixed here since it's a design task outside
|
||||
this phase's scope, not a licensing one.
|
||||
for. Recorded as an action item (replace with an original mark), not a
|
||||
compliance blocker at the time; **resolved as part of the Sentry → Cairn
|
||||
OBS rebrand**, which replaced it with the real Cairn OBS mark from the
|
||||
project's own logo package.
|
||||
|
||||
## Own license declarations (task 5)
|
||||
|
||||
@@ -347,7 +348,7 @@ assumed.
|
||||
- Final repo-wide grep for `commercial` confirms every remaining
|
||||
occurrence is one of the above corrections (explicitly framed as
|
||||
historical/superseded), not a live claim. **No file in the repo claims
|
||||
a license other than AGPLv3** for Sentry's own code, as of this audit.
|
||||
a license other than AGPLv3** for Cairn OBS's own code, as of this audit.
|
||||
|
||||
## Ongoing enforcement (task 7)
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ this project uses it. **Decision recorded 2026-08-16: accept as-is** —
|
||||
see the audit report's Redpanda section for the full reasoning, the
|
||||
other two remediation options that were considered and not chosen, and
|
||||
the condition under which this decision should be revisited (an
|
||||
official hosted/managed Sentry offering). A future change to Redpanda's
|
||||
official hosted/managed Cairn OBS offering). A future change to Redpanda's
|
||||
license, or to this project's own redistribution posture, should trigger
|
||||
re-review, not silently ride on this entry.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sentry Design System (Phase 5)
|
||||
# Cairn OBS Design System (Phase 5)
|
||||
|
||||
Direction: **Signal** — cockpit/ICU-monitor instrumentation logic. Color
|
||||
is a rationed resource: the UI is near-neutral grayscale everywhere, so
|
||||
@@ -140,7 +140,7 @@ Two presets, one token swap — `html.density-compact` overrides
|
||||
and drops `--text-base` to `--text-sm`. Comfortable (the default) suits
|
||||
dashboards and forms; compact suits log tables and query results. Toggle
|
||||
via `$lib/density.svelte.ts`'s `setDensity()`/`toggleDensity()` — a
|
||||
global, persisted (`localStorage['sentry.density']`) preference, not a
|
||||
global, persisted (`localStorage['cairnobs.density']`) preference, not a
|
||||
per-page setting, so switching it on one page carries to the next.
|
||||
`web/src/app.html` has a synchronous inline script that applies the
|
||||
stored value before first paint, so there's no flash of the wrong
|
||||
|
||||
@@ -85,15 +85,15 @@ any agent has sent data.
|
||||
|
||||
## 4. Install the agent's mTLS material
|
||||
|
||||
The agent's default config expects certs at `/etc/sentry-agent/` (see
|
||||
The agent's default config expects certs at `/etc/cairnobs-agent/` (see
|
||||
`/agent/config/agent.example.toml`), which requires root:
|
||||
|
||||
```sh
|
||||
sudo mkdir -p /etc/sentry-agent
|
||||
sudo mkdir -p /etc/cairnobs-agent
|
||||
sudo cp hack/dev-certs/out/ca.pem \
|
||||
hack/dev-certs/out/client.pem \
|
||||
hack/dev-certs/out/client-key.pem \
|
||||
/etc/sentry-agent/
|
||||
/etc/cairnobs-agent/
|
||||
```
|
||||
|
||||
## 5. Build and run the agent
|
||||
@@ -116,7 +116,7 @@ Reading the system journal generally needs root (or membership in the
|
||||
by distro, root is the reliable path for this runbook):
|
||||
|
||||
```sh
|
||||
sudo RUST_LOG=info ./target/release/sentry-agent
|
||||
sudo RUST_LOG=info ./target/release/cairnobs-agent
|
||||
```
|
||||
|
||||
`RUST_LOG=info` matters: `tracing_subscriber`'s default filter is
|
||||
|
||||
@@ -128,14 +128,14 @@ compile time), same requirement as the Linux build.
|
||||
### C2. Get mTLS certs onto the Windows host
|
||||
|
||||
Copy `hack/dev-certs/out/{ca,client,client-key}.pem` from wherever you
|
||||
ran `generate.sh` to `C:\ProgramData\SentryAgent\` on the Windows host
|
||||
ran `generate.sh` to `C:\ProgramData\CairnObsAgent\` on the Windows host
|
||||
(create the directory first). Same dev-only certs Phase 0's Linux agent
|
||||
uses — the CA doesn't care what platform the client is on, only that the
|
||||
client cert was signed by it.
|
||||
|
||||
### C3. Config
|
||||
|
||||
Create `C:\ProgramData\SentryAgent\agent.toml`:
|
||||
Create `C:\ProgramData\CairnObsAgent\agent.toml`:
|
||||
|
||||
```toml
|
||||
[source]
|
||||
@@ -155,7 +155,7 @@ needed (same note as Phase 0's runbook's troubleshooting section).
|
||||
|
||||
```powershell
|
||||
$env:RUST_LOG="info"
|
||||
.\sentry-agent.exe --config C:\ProgramData\SentryAgent\agent.toml
|
||||
.\cairnobs-agent.exe --config C:\ProgramData\CairnObsAgent\agent.toml
|
||||
```
|
||||
|
||||
Confirms the Event Log source and mTLS connection work before adding the
|
||||
@@ -167,7 +167,7 @@ diagnose here than after wrapping it in a service.
|
||||
From another PowerShell window (or Event Viewer):
|
||||
|
||||
```powershell
|
||||
eventcreate /T INFORMATION /ID 1 /L APPLICATION /SO "SentryTest" /D "phase1 windows verification line"
|
||||
eventcreate /T INFORMATION /ID 1 /L APPLICATION /SO "CairnObsTest" /D "phase1 windows verification line"
|
||||
```
|
||||
|
||||
Then check both query paths, same pattern as A2.
|
||||
@@ -175,11 +175,11 @@ Then check both query paths, same pattern as A2.
|
||||
### C6. Install as a service
|
||||
|
||||
```powershell
|
||||
.\sentry-agent.exe install
|
||||
sc.exe start SentryAgent
|
||||
.\cairnobs-agent.exe install
|
||||
sc.exe start CairnObsAgent
|
||||
```
|
||||
|
||||
Verify it's running (`sc.exe query SentryAgent`) and generate another
|
||||
Verify it's running (`sc.exe query CairnObsAgent`) and generate another
|
||||
test event to confirm it's still flowing through while running as a
|
||||
service, not just in the foreground. **Known gap:** no console under the
|
||||
SCM means `tracing`'s log output currently has nowhere to go — see
|
||||
@@ -188,8 +188,8 @@ something goes wrong here, you're debugging blind until that's
|
||||
addressed; C4's foreground run is where to diagnose real problems.
|
||||
|
||||
```powershell
|
||||
sc.exe stop SentryAgent
|
||||
.\sentry-agent.exe uninstall
|
||||
sc.exe stop CairnObsAgent
|
||||
.\cairnobs-agent.exe uninstall
|
||||
```
|
||||
|
||||
### C7 (optional). ETW
|
||||
|
||||
@@ -136,7 +136,7 @@ in the session-local history panel.
|
||||
|
||||
```sh
|
||||
cd cli
|
||||
go run ./cmd/sentryctl query 'service=api | where status>=500 | stats count by host | sort -count'
|
||||
go run ./cmd/cairnobsctl query 'service=api | where status>=500 | stats count by host | sort -count'
|
||||
```
|
||||
|
||||
Both hit the exact same `POST /query` endpoint — there's no separate
|
||||
|
||||
@@ -297,7 +297,7 @@ Loop shape:
|
||||
up to a bounded batch size.
|
||||
2. Dispatch claimed rules to a bounded worker pool (goroutines).
|
||||
3. Each worker: `POST /query` against `api` (reusing the existing
|
||||
endpoint — the same precedent `sentryctl query` already set, never a
|
||||
endpoint — the same precedent `cairnobsctl query` already set, never a
|
||||
second query-execution path), evaluate the condition, run the state
|
||||
transition (fix 3/4 aware) and, if applicable, the fix-2 transactional
|
||||
outbox insert, in one short DB transaction.
|
||||
@@ -319,7 +319,7 @@ loop, and the delivery worker — all four pieces of "alerting" in one
|
||||
service, since they share the same Postgres tables and the same
|
||||
claim-based concurrency pattern. It does **not** import `api`'s
|
||||
`querylang` package or talk to ClickHouse/Tantivy directly; it only
|
||||
calls `api`'s `POST /query` over HTTP, exactly like `sentryctl query`
|
||||
calls `api`'s `POST /query` over HTTP, exactly like `cairnobsctl query`
|
||||
and the web UI's dashboard panels already do. `web` gets a second
|
||||
backend base URL (`alerting`'s) alongside the existing `api` one.
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ per panel directly, exactly the same endpoint the root query page already
|
||||
uses. This keeps `api/internal/dashboards` pure CRUD with zero
|
||||
query-execution code of its own, consistent with the project's standing
|
||||
rule that nothing duplicates `querylang`'s execution path (the same
|
||||
reason `sentryctl query` calls `/query` over HTTP instead of importing
|
||||
reason `cairnobsctl query` calls `/query` over HTTP instead of importing
|
||||
`querylang` internals). It also means panels load and error
|
||||
independently in the UI — one broken panel query doesn't fail the whole
|
||||
dashboard.
|
||||
@@ -253,7 +253,7 @@ used internally, not a separate export-specific shape). `POST
|
||||
/dashboards/import` accepts that same shape and creates a new dashboard
|
||||
from it (new IDs assigned, not a literal replay of the source IDs, so
|
||||
importing into a different environment doesn't collide). This is
|
||||
deliberately the *same* JSON contract `sentryctl dashboards apply` (task
|
||||
deliberately the *same* JSON contract `cairnobsctl dashboards apply` (task
|
||||
7) consumes — "exportable/importable, Terraform-friendly" isn't a
|
||||
separate design, it's one JSON shape used from two call sites (web
|
||||
export button, CLI apply).
|
||||
|
||||
+52
-52
@@ -95,7 +95,7 @@ memberships and getting back the right tenant/role each time (see §3a,
|
||||
`helm` were installed without root (`kind`/`kubectl` as static binaries,
|
||||
`helm` the same, all into `~/.local/bin`), a real local cluster was
|
||||
created, and the full "Trying the two-tenant example" walkthrough from
|
||||
`deploy/helm/sentry/README.md` was run end to end against it -- both
|
||||
`deploy/helm/cairnobs/README.md` was run end to end against it -- both
|
||||
`acme` and `globex` reached `Tenant.status.phase: Active` with real
|
||||
generated ClickHouse credentials in their Secrets. That run found and
|
||||
fixed two more real bugs, neither ever caught before because this chart
|
||||
@@ -138,7 +138,7 @@ New service beyond Phase 3: `enterprise-auth` (port 8082) — see
|
||||
`enterprise/README.md`. Not wired into `api`/`alerting`'s enforcement by
|
||||
default (`docker-compose.yml`'s comment on why: no OIDC/SAML login flow
|
||||
exists yet, so turning on enforcement by default would break the web UI
|
||||
and `sentryctl` with no way to log in).
|
||||
and `cairnobsctl` with no way to log in).
|
||||
|
||||
## 2. Confirm Phase 0-3 behavior is unchanged
|
||||
|
||||
@@ -148,7 +148,7 @@ through every piece of Phase 4 auth wiring:
|
||||
|
||||
```sh
|
||||
curl -s -X POST http://localhost:8080/query -H 'Content-Type: application/json' -d '{"query":"stats count"}'
|
||||
sentryctl dashboards list
|
||||
cairnobsctl dashboards list
|
||||
curl -s http://localhost:8081/healthz
|
||||
```
|
||||
|
||||
@@ -192,7 +192,7 @@ creating the `users` row via `UpsertUserBySSO`, exactly as designed;
|
||||
after `-grant-membership-*` granted that real Auth0 identity
|
||||
(`[email protected]`) an `admin` role on `acme`, a second login
|
||||
completed the full OAuth code exchange, consent screen, and redirect to
|
||||
`web`, landing a real `sentry_session` cookie; `POST
|
||||
`web`, landing a real `cairnobs_session` cookie; `POST
|
||||
/internal/authorize` with that cookie returned
|
||||
`{"tenant_id":"acme","user_id":"...","role":"admin"}` -- the exact
|
||||
membership granted, round-tripped through a real external IdP, not a
|
||||
@@ -232,7 +232,7 @@ docker compose run --rm enterprise-auth \
|
||||
Then visit `http://localhost:8082/auth/oidc/login` again in a real
|
||||
browser, complete the IdP's login, and confirm you land on
|
||||
`POST_LOGIN_REDIRECT_URL` (`http://localhost:3000` by default) with a
|
||||
`sentry_session` cookie set. `-create-tenant` only touches `rbacstore`
|
||||
`cairnobs_session` cookie set. `-create-tenant` only touches `rbacstore`
|
||||
(control-plane/RBAC) -- it's independent of `enterprise-api
|
||||
-provision-tenant`'s ClickHouse/Tantivy data-plane provisioning (§8),
|
||||
so a tenant created this way can log users in immediately but can't yet
|
||||
@@ -243,7 +243,7 @@ cover revoking, listing, and reassigning ownership the same way
|
||||
`-grant-membership-*` covers granting (all offline operator flags, same
|
||||
shape as `-create-tenant`) -- `dashboard_permissions` grants (§5a) are
|
||||
the one thing here still only reachable via the HTTP endpoints
|
||||
directly, no operator flag, since `sentryctl dashboards permissions
|
||||
directly, no operator flag, since `cairnobsctl dashboards permissions
|
||||
list|grant|revoke` already exists as that surface instead (§5a).
|
||||
|
||||
## 3b. `enterprise-auth`: human login via SAML (now genuinely verified
|
||||
@@ -294,7 +294,7 @@ offer a free developer/trial tenant with SAML app support):
|
||||
# SAML_IDP_METADATA_URL: "https://your-idp.example.com/metadata"
|
||||
# Register SAML_ENTITY_ID (as "audience")/SAML_ACS_URL (as the
|
||||
# "Application Callback URL") with the IdP's application config -- the
|
||||
# IdP needs Sentry's ACS URL to know where to POST the assertion, and
|
||||
# IdP needs Cairn OBS's ACS URL to know where to POST the assertion, and
|
||||
# the two must actually agree or the SP-side validation fails with a
|
||||
# generic "Authentication failed" (found the hard way -- crewjam/saml's
|
||||
# error here doesn't distinguish audience mismatch from other causes).
|
||||
@@ -309,7 +309,7 @@ Bootstrapping the first `tenant_memberships` row uses the same
|
||||
`-create-tenant`/`-grant-membership-*` flags as §3a (log in once, it
|
||||
fails with 403, grant the membership using the email you logged in
|
||||
with, log in again). Then visit `/auth/saml/login` on your HTTPS host in
|
||||
a real browser, complete the IdP's login, and confirm a `sentry_session`
|
||||
a real browser, complete the IdP's login, and confirm a `cairnobs_session`
|
||||
cookie lands after redirect to `POST_LOGIN_REDIRECT_URL`.
|
||||
|
||||
## 4. Turn on RBAC enforcement and prove it actually blocks/allows
|
||||
@@ -318,7 +318,7 @@ Without touching the main stack's `api` container (so step 2's baseline
|
||||
keeps working):
|
||||
|
||||
```sh
|
||||
docker compose run --rm -d --name sentry-api-enforced -p 8090:8080 \
|
||||
docker compose run --rm -d --name cairnobs-api-enforced -p 8090:8080 \
|
||||
-e ENTERPRISE_AUTH_URL=http://enterprise-auth:8082 api
|
||||
|
||||
curl -s -o /dev/null -w "no auth -> %{http_code} (want 401)\n" \
|
||||
@@ -327,7 +327,7 @@ curl -s -o /dev/null -w "with service token -> %{http_code} (want 200)\n" \
|
||||
-X POST http://localhost:8090/query -H "Authorization: Bearer $TOKEN" \
|
||||
-H 'Content-Type: application/json' -d '{"query":"stats count"}'
|
||||
|
||||
docker stop sentry-api-enforced
|
||||
docker stop cairnobs-api-enforced
|
||||
```
|
||||
|
||||
`GET /dashboards` on the same enforced instance should return 401
|
||||
@@ -346,7 +346,7 @@ tenant. Verify the real SQL, not just the fake-store unit tests:
|
||||
```sh
|
||||
docker run --rm --network sentry_default -v $(pwd)/api:/src -w /src \
|
||||
-e DASHBOARDS_TEST_POSTGRES_ADDR=metadata-postgres:5432 \
|
||||
-e DASHBOARDS_TEST_POSTGRES_PASSWORD=sentry-dev-only \
|
||||
-e DASHBOARDS_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./dashboards/... -run Integration -v
|
||||
```
|
||||
|
||||
@@ -375,7 +375,7 @@ PermissionStore`) have real integration tests, same skip-gated shape as
|
||||
```sh
|
||||
docker run --rm --network sentry_default -v $(pwd):/src -w /src/enterprise \
|
||||
-e RBACSTORE_TEST_POSTGRES_ADDR=metadata-postgres:5432 \
|
||||
-e RBACSTORE_TEST_POSTGRES_PASSWORD=sentry-dev-only \
|
||||
-e RBACSTORE_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./internal/rbacstore/... -run DashboardPermission -v
|
||||
```
|
||||
|
||||
@@ -396,13 +396,13 @@ when `enterprise-api` (not plain `api`) is serving traffic -- see
|
||||
```sh
|
||||
docker run --rm --network sentry_default -v $(pwd):/src -w /src/enterprise \
|
||||
-e RBACSTORE_TEST_POSTGRES_ADDR=metadata-postgres:5432 \
|
||||
-e RBACSTORE_TEST_POSTGRES_PASSWORD=sentry-dev-only \
|
||||
-e RBACSTORE_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./internal/rbacstore/... -v
|
||||
|
||||
docker run --rm --network sentry_default -v $(pwd):/src -w /src/enterprise \
|
||||
-e AUDIT_TEST_POSTGRES_ADDR=metadata-postgres:5432 \
|
||||
-e AUDIT_TEST_POSTGRES_PASSWORD=audit-writer-dev-only \
|
||||
-e AUDIT_TEST_ADMIN_PASSWORD=sentry-dev-only \
|
||||
-e AUDIT_TEST_ADMIN_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./internal/audit/... -v
|
||||
```
|
||||
|
||||
@@ -419,10 +419,10 @@ Offline checks (no cluster needed):
|
||||
```sh
|
||||
cd deploy/operator && go build ./... && go vet ./... && go test ./...
|
||||
|
||||
cd ../helm/sentry
|
||||
cd ../helm/cairnobs
|
||||
helm lint .
|
||||
helm template sentry . --include-crds > /tmp/default.yaml
|
||||
helm template sentry . --include-crds \
|
||||
helm template cairnobs . --include-crds > /tmp/default.yaml
|
||||
helm template cairnobs . --include-crds \
|
||||
--set enterprise.enabled=true --set tenantOperator.enabled=true \
|
||||
--set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \
|
||||
--set 'tenants[1].name=globex' --set 'tenants[1].displayName=Globex Corporation' \
|
||||
@@ -434,25 +434,25 @@ without root (`kind`/`kubectl`/`helm` as static binaries into e.g.
|
||||
`~/.local/bin`; no package manager or sudo needed):
|
||||
|
||||
```sh
|
||||
kind create cluster --name sentry-phase4
|
||||
kind create cluster --name cairnobs-phase4
|
||||
kubectl wait --for=condition=Ready node --all --timeout=120s
|
||||
|
||||
# Build and load every image the chart references -- kind's nodes can't
|
||||
# pull unpublished local-only images from a registry, only from images
|
||||
# already loaded into the node directly.
|
||||
docker build -f deploy/operator/Dockerfile -t sentry-tenant-operator deploy/operator/
|
||||
for img in sentry-redpanda-provision sentry-clickhouse-migrate sentry-metadata-migrate \
|
||||
sentry-ingest sentry-search sentry-api sentry-alerting sentry-web \
|
||||
sentry-enterprise-auth sentry-enterprise-api sentry-enterprise-ingest \
|
||||
sentry-tenant-operator; do
|
||||
kind load docker-image "${img}:latest" --name sentry-phase4
|
||||
docker build -f deploy/operator/Dockerfile -t cairnobs-tenant-operator deploy/operator/
|
||||
for img in cairnobs-redpanda-provision cairnobs-clickhouse-migrate cairnobs-metadata-migrate \
|
||||
cairnobs-ingest cairnobs-search cairnobs-api cairnobs-alerting cairnobs-web \
|
||||
cairnobs-enterprise-auth cairnobs-enterprise-api cairnobs-enterprise-ingest \
|
||||
cairnobs-tenant-operator; do
|
||||
kind load docker-image "${img}:latest" --name cairnobs-phase4
|
||||
done
|
||||
|
||||
# NOTE: helm install has no --include-crds flag (that's a helm template-only
|
||||
# flag -- install always installs crds/ by default). The command in
|
||||
# deploy/helm/sentry/README.md's "Trying the two-tenant example" had this
|
||||
# deploy/helm/cairnobs/README.md's "Trying the two-tenant example" had this
|
||||
# wrong; fixed there too.
|
||||
helm install sentry deploy/helm/sentry \
|
||||
helm install cairnobs deploy/helm/cairnobs \
|
||||
--set enterprise.enabled=true --set tenantOperator.enabled=true \
|
||||
--set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \
|
||||
--set 'tenants[1].name=globex' --set 'tenants[1].displayName=Globex Corporation'
|
||||
@@ -467,12 +467,12 @@ equivalent) rather than hand-rolling it in the chart, so a Secret needs
|
||||
supplying before `ingest` can start:
|
||||
|
||||
```sh
|
||||
kubectl create secret generic sentry-ingest-tls \
|
||||
kubectl create secret generic cairnobs-ingest-tls \
|
||||
--from-file=server.pem=hack/dev-certs/out/server.pem \
|
||||
--from-file=server-key.pem=hack/dev-certs/out/server-key.pem \
|
||||
--from-file=ca.pem=hack/dev-certs/out/ca.pem
|
||||
helm upgrade sentry deploy/helm/sentry --reuse-values \
|
||||
--set ingest.tlsSecretName=sentry-ingest-tls
|
||||
helm upgrade cairnobs deploy/helm/cairnobs --reuse-values \
|
||||
--set ingest.tlsSecretName=cairnobs-ingest-tls
|
||||
```
|
||||
|
||||
Confirm every pod actually reaches `Running`/`1/1` (`kubectl get pods`)
|
||||
@@ -486,10 +486,10 @@ missing `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT` env var (same bug
|
||||
§1's `docker-compose.yml` had). Both fixed in the chart itself.
|
||||
|
||||
```sh
|
||||
kubectl exec -it deploy/sentry-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp"
|
||||
kubectl exec -it deploy/sentry-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation"
|
||||
kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp"
|
||||
kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation"
|
||||
kubectl get tenants
|
||||
kubectl get secret sentry-tenant-acme-clickhouse sentry-tenant-globex-clickhouse -o yaml
|
||||
kubectl get secret cairnobs-tenant-acme-clickhouse cairnobs-tenant-globex-clickhouse -o yaml
|
||||
```
|
||||
|
||||
**Confirmed live**: `kubectl get tenants` shows both `acme` and `globex`
|
||||
@@ -538,12 +538,12 @@ walkthrough isn't scripted yet):
|
||||
```sh
|
||||
docker run --rm --network sentry_default -v $(pwd)/enterprise:/src -w /src \
|
||||
-e CHRUNNER_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \
|
||||
-e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \
|
||||
-e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./internal/chrunner/... -v
|
||||
|
||||
docker run --rm --network sentry_default -v $(pwd)/enterprise:/src -w /src \
|
||||
-e TENANTPROVISION_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \
|
||||
-e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \
|
||||
-e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./internal/tenantprovision/... -v
|
||||
```
|
||||
|
||||
@@ -606,9 +606,9 @@ No live cluster needed for this either — `helm template`'s output is
|
||||
plain YAML, parseable without a cluster:
|
||||
|
||||
```sh
|
||||
cd deploy/helm/sentry
|
||||
helm template sentry . --include-crds > /tmp/default.yaml
|
||||
helm template sentry . --include-crds --set enterprise.enabled=true \
|
||||
cd deploy/helm/cairnobs
|
||||
helm template cairnobs . --include-crds > /tmp/default.yaml
|
||||
helm template cairnobs . --include-crds --set enterprise.enabled=true \
|
||||
--set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \
|
||||
> /tmp/enterprise.yaml
|
||||
|
||||
@@ -616,17 +616,17 @@ python3 -c "
|
||||
import yaml
|
||||
for f in ['/tmp/default.yaml', '/tmp/enterprise.yaml']:
|
||||
docs = list(yaml.safe_load_all(open(f)))
|
||||
deploys = [d for d in docs if d and d.get('kind')=='Deployment' and d.get('metadata',{}).get('name')=='sentry-api']
|
||||
deploys = [d for d in docs if d and d.get('kind')=='Deployment' and d.get('metadata',{}).get('name')=='cairnobs-api']
|
||||
print(f, '->', [d['spec']['template']['spec']['containers'][0]['image'] for d in deploys])
|
||||
"
|
||||
# expect: default.yaml -> ['sentry-api:latest'], enterprise.yaml -> ['sentry-enterprise-api:latest']
|
||||
# and exactly one Deployment named sentry-api in each file.
|
||||
# expect: default.yaml -> ['cairnobs-api:latest'], enterprise.yaml -> ['cairnobs-enterprise-api:latest']
|
||||
# and exactly one Deployment named cairnobs-api in each file.
|
||||
```
|
||||
|
||||
Real cluster (`kind create cluster`, or similar): `helm install` with
|
||||
each set of values and confirm `kubectl get deploy sentry-api -o
|
||||
each set of values and confirm `kubectl get deploy cairnobs-api -o
|
||||
jsonpath='{.spec.template.spec.containers[0].image}'` matches, and that
|
||||
`kubectl get svc sentry-api` routes to whichever one is actually running.
|
||||
`kubectl get svc cairnobs-api` routes to whichever one is actually running.
|
||||
|
||||
## 10a. Confirm `docker-compose.yml` now enforces the same binary swap
|
||||
|
||||
@@ -668,7 +668,7 @@ actually start and route traffic correctly end to end.
|
||||
|
||||
## 11. `Tenant` CRD unified with `-provision-tenant`
|
||||
|
||||
`deploy/helm/sentry/README.md`'s "Trying the two-tenant example" section
|
||||
`deploy/helm/cairnobs/README.md`'s "Trying the two-tenant example" section
|
||||
has the full `helm install` → `-provision-tenant` → `kubectl get
|
||||
tenants` walkthrough. What was actually run in this environment (no
|
||||
live cluster, same limitation as §10):
|
||||
@@ -762,7 +762,7 @@ through `mcp__claude-in-chrome`:
|
||||
1. A throwaway Node HTTP server (no dependencies) stood in for
|
||||
`enterprise-auth`, implementing the exact wire contract this section's
|
||||
Go tests already prove server-side: `GET /auth/memberships` and
|
||||
`POST /auth/select-tenant`, the `sentry_pending_login` cookie
|
||||
`POST /auth/select-tenant`, the `cairnobs_pending_login` cookie
|
||||
(`Path=/auth`), the credentialed CORS headers, and critically the
|
||||
*plain-text* `http.Error` response bodies the real handler sends on
|
||||
failure (not JSON -- `enterpriseAuthRequest` in `$lib/api.ts` reads
|
||||
@@ -857,7 +857,7 @@ Also not built as part of the identity mechanism itself: any Helm/
|
||||
`docker-compose.yml` wiring that issues an agent a real ingest credential
|
||||
automatically (`enterprise-auth -create-ingest-credential-tenant=<id>`
|
||||
is, like every other credential-minting flag in this codebase, a manual
|
||||
operator action) -- `deploy/helm/sentry/values.yaml`'s
|
||||
operator action) -- `deploy/helm/cairnobs/values.yaml`'s
|
||||
`ingest.requireTenantCredential` (default `false`) only turns on
|
||||
*validation*, deliberately not folded into `enterprise.enabled`
|
||||
directly, since flipping that flag with no agents holding a credential
|
||||
@@ -1022,7 +1022,7 @@ that's never executed against a real database. See
|
||||
|
||||
**Also not built**: Helm/`docker-compose.yml` do gate *whether*
|
||||
`enterprise-ingest` runs at all (`ingest.requireTenantCredential`, same
|
||||
flag §13 uses for validation -- see `deploy/helm/sentry/templates/
|
||||
flag §13 uses for validation -- see `deploy/helm/cairnobs/templates/
|
||||
enterprise-ingest.yaml`), but `docker-compose.yml`'s version is a
|
||||
disclosed, weaker approximation of Helm's: Helm achieves genuine
|
||||
`-mode=server`/`-mode=consumer` mutual exclusivity between `ingest` and
|
||||
@@ -1040,7 +1040,7 @@ Full accounting: `/docs/security/threat-model.md`. Headline items:
|
||||
|
||||
- **Both storage engines' isolation exists, and both Helm and
|
||||
docker-compose now enforce which binary runs.**
|
||||
`deploy/helm/sentry/templates/api.yaml`/`enterprise-api.yaml` are
|
||||
`deploy/helm/cairnobs/templates/api.yaml`/`enterprise-api.yaml` are
|
||||
mutually exclusive on `enterprise.enabled` (§10) -- a Helm-deployed
|
||||
cluster can't accidentally run the non-isolated binary once that flag
|
||||
is set. `docker-compose.yml`'s `api`/`enterprise-api` services are now
|
||||
@@ -1120,14 +1120,14 @@ Full accounting: `/docs/security/threat-model.md`. Headline items:
|
||||
handler reads `dashboard_permissions` via
|
||||
`enterprise/internal/rbacstore.DashboardPermissions`, only when
|
||||
`enterprise-api` -- not plain `api` -- serves traffic), **and now has a
|
||||
CLI surface**: `sentryctl dashboards permissions list|grant|revoke`
|
||||
(`cli/cmd/sentryctl/cmd_dashboards.go`) against
|
||||
CLI surface**: `cairnobsctl dashboards permissions list|grant|revoke`
|
||||
(`cli/cmd/cairnobsctl/cmd_dashboards.go`) against
|
||||
`GET`/`PUT`/`DELETE /dashboards/{id}/permissions/{userId}` -- still no
|
||||
`web` UI for it, just the CLI. Verified against a real `httptest.
|
||||
Server` (not a fake store this time -- the CLI has no store of its
|
||||
own, just an HTTP client, so this is exercising real request
|
||||
construction/method/path/body/error-parsing, the same pattern every
|
||||
other `sentryctl` subcommand's tests use):
|
||||
other `cairnobsctl` subcommand's tests use):
|
||||
|
||||
```sh
|
||||
cd cli
|
||||
@@ -1162,7 +1162,7 @@ Full accounting: `/docs/security/threat-model.md`. Headline items:
|
||||
|
||||
```sh
|
||||
docker compose down -v
|
||||
helm uninstall sentry # if installed against a real cluster
|
||||
helm uninstall cairnobs # if installed against a real cluster
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -1191,6 +1191,6 @@ parameter.
|
||||
|
||||
**`helm template` fails with `error calling include: ... can't evaluate
|
||||
field Release in type string`.**
|
||||
A call site is passing a bare string to `sentry.selectorLabels` instead
|
||||
A call site is passing a bare string to `cairnobs.selectorLabels` instead
|
||||
of `(list $ "name")` — see `templates/_helpers.tpl`'s doc comment for
|
||||
why the plain-string form doesn't work with `include`.
|
||||
|
||||
@@ -63,7 +63,7 @@ Design choices worth calling out explicitly:
|
||||
| Throughput under concurrency | Adequate for one-user-at-a-time interactive use; not built for high concurrent QPS | Purpose-built for high-throughput serving (continuous batching, PagedAttention) |
|
||||
| Fit for this project | Matches `docker-compose for local/homelab` (CLAUDE.md's stated deployment target) — most self-hosters won't have a dedicated inference GPU | Fits a provisioned-GPU SaaS inference tier — not this phase's target (cloud is the opt-in secondary path, not primary) |
|
||||
|
||||
Sentry's actual AI workload shape is one interactive query bar per user
|
||||
Cairn OBS's actual AI workload shape is one interactive query bar per user
|
||||
at a time, not a high-QPS inference-serving problem — vLLM's real
|
||||
advantage (batched throughput at scale) isn't the bottleneck this phase
|
||||
has. Ollama's lower hardware floor and much simpler operational story
|
||||
|
||||
@@ -191,7 +191,7 @@ cd enterprise && go build ./... && go vet ./... && go test ./...
|
||||
docker run --rm --network sentry_default -v "$(pwd):/src" -w /src/enterprise \
|
||||
-e AUDIT_TEST_POSTGRES_ADDR=metadata-postgres:5432 \
|
||||
-e AUDIT_TEST_POSTGRES_PASSWORD=audit-writer-dev-only \
|
||||
-e AUDIT_TEST_ADMIN_PASSWORD=sentry-dev-only \
|
||||
-e AUDIT_TEST_ADMIN_PASSWORD=cairnobs-dev-only \
|
||||
golang:1.25-alpine go test ./internal/audit/... -v
|
||||
|
||||
# cli module
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Query language reference
|
||||
|
||||
Sentry has one query language for everything: filtering, free-text
|
||||
Cairn OBS has one query language for everything: filtering, free-text
|
||||
search, and aggregation, in a single query, against a single endpoint
|
||||
(`POST /query`), from a single query bar in the web UI or `sentryctl
|
||||
(`POST /query`), from a single query bar in the web UI or `cairnobsctl
|
||||
query` on the command line. You don't pick a "search mode" or a
|
||||
"reporting mode" first — you write one query, and Sentry figures out
|
||||
"reporting mode" first — you write one query, and Cairn OBS figures out
|
||||
which parts need ClickHouse, which parts need the full-text index, and
|
||||
combines them.
|
||||
|
||||
If you already know Splunk's SPL, most of this will feel immediately
|
||||
familiar: a base search, piped through a sequence of processing stages.
|
||||
Sentry's language is a deliberately smaller subset — the operators
|
||||
Cairn OBS's language is a deliberately smaller subset — the operators
|
||||
people actually use day to day, not SPL's full surface area — plus raw
|
||||
SQL as an escape hatch for anything the pipe syntax doesn't (yet) cover.
|
||||
|
||||
@@ -67,7 +67,7 @@ timeout a single bare word
|
||||
message:"connection refused" the same thing, explicit
|
||||
```
|
||||
|
||||
Free-text search is powered by Sentry's full-text index (Tantivy), which
|
||||
Free-text search is powered by Cairn OBS's full-text index (Tantivy), which
|
||||
supports phrase matching and wildcards:
|
||||
|
||||
```
|
||||
@@ -92,7 +92,7 @@ what most people expect from a search bar:
|
||||
error timeout same as: error and timeout
|
||||
```
|
||||
|
||||
`or` works between free-text terms, and Sentry's full-text index handles
|
||||
`or` works between free-text terms, and Cairn OBS's full-text index handles
|
||||
it natively:
|
||||
|
||||
```
|
||||
@@ -176,14 +176,14 @@ tail 50 last 50, chronologically
|
||||
|
||||
## Field mapping: what's a "real" column vs. an attribute
|
||||
|
||||
Sentry's structured columns are `timestamp`, `host`, `service`,
|
||||
Cairn OBS's structured columns are `timestamp`, `host`, `service`,
|
||||
`severity`, `message`, and `record_id`. Anything else you reference by
|
||||
name — `status`, `latency_ms`, `winevt.event_id`, whatever your logs
|
||||
happen to carry — is looked up in the per-record attributes, which are
|
||||
always stored as text.
|
||||
|
||||
This matters for comparisons: `status>=500` only makes sense as a number,
|
||||
so Sentry casts the attribute's text value to a number for you
|
||||
so Cairn OBS casts the attribute's text value to a number for you
|
||||
automatically when the value you're comparing against looks numeric.
|
||||
`status="unknown"` compares as text instead, since `"unknown"` isn't a
|
||||
number. You don't need to do anything differently — this happens based
|
||||
@@ -212,7 +212,7 @@ directly, no pipe-syntax parsing involved:
|
||||
SELECT host, count(*) FROM logs WHERE service = 'api' GROUP BY host
|
||||
```
|
||||
|
||||
SELECT-only, single statement — Sentry allowlists this at the API level.
|
||||
SELECT-only, single statement — Cairn OBS allowlists this at the API level.
|
||||
Use this for anything the pipe syntax doesn't cover yet: window
|
||||
functions, `WITH` clauses, ClickHouse-specific functions, joins across
|
||||
other tables you've added, and so on. There's no performance penalty for
|
||||
@@ -221,7 +221,7 @@ execution plan internally.
|
||||
|
||||
## Which syntax am I using?
|
||||
|
||||
Sentry detects automatically: a query starting with `SELECT` runs as
|
||||
Cairn OBS detects automatically: a query starting with `SELECT` runs as
|
||||
SQL, anything else runs as the pipe syntax. This covers the overwhelming
|
||||
majority of real queries with no extra step. If you're writing a pipe
|
||||
query that happens to start with the literal word "select" as a search
|
||||
@@ -237,7 +237,7 @@ detected next to the query box, with a dropdown to override it.
|
||||
|
||||
## Combining free-text search with aggregation
|
||||
|
||||
This is the case that makes Sentry's query language more than "SQL with
|
||||
This is the case that makes Cairn OBS's query language more than "SQL with
|
||||
extra steps" — free text and aggregation, together, in one query:
|
||||
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sentry Threat Model (Phase 4)
|
||||
# Cairn OBS Threat Model (Phase 4)
|
||||
|
||||
Written for a prospective enterprise customer's security team, describing
|
||||
the system **as actually built** through Phase 4 task 7 — not the target
|
||||
|
||||
Reference in New Issue
Block a user