Finish the Cairn OBS rename through services, docs, and assets
The rename commit before this one covered module paths and the obvious user-facing strings; this is the rest of it -- the places where "sentry" was a default value, a filename, or a picture rather than a word in a sentence. Defaults that changed: CLICKHOUSE_DATABASE (sentry -> cairnobs), POSTGRES_DATABASE (sentry_metadata -> cairnobs_metadata), and POSTGRES_USERNAME (sentry -> cairnobs), across api/alerting/ingest and the enterprise binaries, plus the compose files and migrate scripts that create those objects. These are *defaults*, so a deployment that sets them explicitly is unaffected -- but any deployment relying on the old defaults must have its environment updated before it picks this up, or it will come up pointing at a database that doesn't exist. Also: the light-mode logo variants (the dark ones existed alone, so the landing page and sidebar rendered a dark mark on a light background), regenerated favicons, and the docs/README/threat-model prose that still said Sentry.
This commit is contained in:
@@ -135,7 +135,7 @@ already configured for.
|
||||
## Data model
|
||||
|
||||
`metadata/migrations/0037_create_agents.sql`: one `agents` table, one
|
||||
row per `(tenant_id, host)`, in the same `sentry_metadata` Postgres
|
||||
row per `(tenant_id, host)`, in the same `cairnobs_metadata` Postgres
|
||||
dashboards/alert_rules already live in — not a new database, matching
|
||||
this project's established "shared schema, different services own
|
||||
different tables" shape. `tenant_id` defaults to `'default'` for
|
||||
@@ -257,18 +257,18 @@ the process exits cleanly -- `pending_command` confirmed cleared and
|
||||
|
||||
## CLI surface (punch-list item 3)
|
||||
|
||||
`sentryctl agents` (`cli/cmd/sentryctl/cmd_agents.go`), same list/get
|
||||
`cairnobsctl agents` (`cli/cmd/cairnobsctl/cmd_agents.go`), same list/get
|
||||
shape as `dashboards`/`alerts`, plus a `config` sub-subcommand
|
||||
(mirroring `dashboards permissions`) since an override has its own
|
||||
get/set/clear lifecycle distinct from the agent resource itself:
|
||||
|
||||
```
|
||||
sentryctl agents list|get <host>
|
||||
sentryctl agents config get <host>|clear <host>
|
||||
sentryctl agents config set <host> [--batch-max-size N] [--batch-flush-interval-ms N]
|
||||
cairnobsctl agents list|get <host>
|
||||
cairnobsctl agents config get <host>|clear <host>
|
||||
cairnobsctl agents config set <host> [--batch-max-size N] [--batch-flush-interval-ms N]
|
||||
[--heartbeat-enabled true|false] [--heartbeat-interval-ms N]
|
||||
[--journald-unit UNIT]
|
||||
sentryctl agents restart <host> [--yes]
|
||||
cairnobsctl agents restart <host> [--yes]
|
||||
```
|
||||
|
||||
`config set` is the one command with real logic beyond a thin HTTP
|
||||
|
||||
@@ -81,7 +81,7 @@ This split is not to be changed without discussion — see CLAUDE.md.
|
||||
| `alerting` (Go, Phase 3) | Evaluates alert rules on an interval, calls `api`'s `POST /query` (via a `RoleService` credential once Phase 4 auth is configured — see `/docs/phase-4-isolation-design.md`'s alerting↔api gap), delivers firing/resolved notifications (webhook/Slack/PagerDuty). |
|
||||
| `enterprise` (Go, AGPLv3 — see "Licensing boundary" below, Phase 4) | OIDC login (`internal/loginhandler`'s `/auth/oidc/login`+`/auth/oidc/callback`) and SAML login (`/auth/saml/login`+`/auth/saml/acs`, via `internal/saml`'s `crewjam/saml` wiring) — both a real IdP round trip, each verified with a real fake IdP (`coreos/go-oidc`'s `oidctest`, `crewjam/saml`'s `samlidp`) but not a real external one, RBAC storage (`internal/rbacstore`), session/service-token issuance (`internal/session`), the append-only audit log (`internal/audit`), `enterprise-auth`'s HTTP surface (`/internal/authorize`, `/auth/features`), per-tenant ClickHouse provisioning (`internal/tenantprovision`) and query routing (`internal/chrunner`), and `cmd/enterprise-api` — a second binary combining core's `api/queryapi`/`api/dashboards` handlers with these tenant-aware implementations. Never imported by core — see "Licensing boundary" below. Also `internal/searchclient` (per-tenant Tantivy routing, wired the same way into `search`). |
|
||||
| `web` (SvelteKit, static build) | Query bar, dashboards, alerts, and (Phase 4) a settings page that renders SSO status via a runtime capability check (`GET /auth/features`) rather than bundling `enterprise/`'s components directly — an architectural choice (core builds and runs standalone) that predates and doesn't depend on Phase 6's relicensing. |
|
||||
| `cli` (`sentryctl`) | `ping`, `query`, `dashboards` (list/get/apply), `alerts` (list/get/apply). `$SENTRYCTL_TOKEN`, if set, is forwarded as a Bearer credential (Phase 4). |
|
||||
| `cli` (`cairnobsctl`) | `ping`, `query`, `dashboards` (list/get/apply), `alerts` (list/get/apply). `$CAIRNOBSCTL_TOKEN`, if set, is forwarded as a Bearer credential (Phase 4). |
|
||||
| `deploy` | A Helm chart covering every `docker-compose.yml` service, plus (Phase 4) a small Go Operator managing one CRD (`Tenant`) that provisions a per-tenant ClickHouse credential Secret. Never applied to a live cluster in the environment this was built in — see `/deploy/README.md`'s verification section before trusting it. |
|
||||
|
||||
## Tenant isolation model (Phase 4)
|
||||
@@ -199,7 +199,7 @@ escape hatch is opaque to any compiler-injected filter.
|
||||
new credentials and still never touches ClickHouse/Postgres.
|
||||
|
||||
**The deployment-topology gap is closed for both Helm and
|
||||
docker-compose**: `deploy/helm/sentry/templates/api.yaml`/
|
||||
docker-compose**: `deploy/helm/cairnobs/templates/api.yaml`/
|
||||
`enterprise-api.yaml` are mutually exclusive on `enterprise.enabled`,
|
||||
rendering to the same Service name and port either way, so a
|
||||
Helm-deployed cluster can't accidentally run the wrong binary — the same
|
||||
|
||||
@@ -8510,7 +8510,7 @@
|
||||
"direct_or_transitive": "direct",
|
||||
"ecosystem": "docker-image",
|
||||
"flagged": "True",
|
||||
"flag_reason": "Category (c): BSL is explicitly source-available, not OSI-approved open source, per the audit's own classification framework. Verified against the actual licenses/bsl.md at the v24.2.7 tag, not assumed. No AGPL linking-compatibility issue (Redpanda is consumed only over the Kafka wire protocol, never linked into Sentry's own code -- same relationship as ClickHouse/Postgres). Real open question: whether a third party self-hosting Sentry (now fully AGPLv3 per task 6) 'as a service' would trip BSL's Streaming-or-Queuing-Service restriction on the bundled Redpanda image -- Sentry's ingest pipeline creates fixed internal topics, not per-end-user topics, so this is very likely NOT a Streaming-or-Queuing-Service under BSL's own definition, but this is a business/redistribution judgment call, not a pure technical one -- flagged for your decision, not resolved unilaterally. See license-audit-report.md's Redpanda section for the three remediation options (accept as-is / swap to Apache Kafka / flag only). RESOLVED 2026-08-16 (business decision): accept as-is. Sentry's own use (internal Kafka-protocol transport, no resale of broker access) is within BSL's Additional Use Grant; the third-party self-hosting-as-a-service question was judged unlikely to trip BSL's Streaming-or-Queuing-Service restriction, and was accepted as a known, disclosed risk rather than swapping to a heavier broker or dropping the bundled image. See license-audit-report.md's Redpanda section.",
|
||||
"flag_reason": "Category (c): BSL is explicitly source-available, not OSI-approved open source, per the audit's own classification framework. Verified against the actual licenses/bsl.md at the v24.2.7 tag, not assumed. No AGPL linking-compatibility issue (Redpanda is consumed only over the Kafka wire protocol, never linked into Cairn OBS's own code -- same relationship as ClickHouse/Postgres). Real open question: whether a third party self-hosting Cairn OBS (now fully AGPLv3 per task 6) 'as a service' would trip BSL's Streaming-or-Queuing-Service restriction on the bundled Redpanda image -- Cairn OBS's ingest pipeline creates fixed internal topics, not per-end-user topics, so this is very likely NOT a Streaming-or-Queuing-Service under BSL's own definition, but this is a business/redistribution judgment call, not a pure technical one -- flagged for your decision, not resolved unilaterally. See license-audit-report.md's Redpanda section for the three remediation options (accept as-is / swap to Apache Kafka / flag only). RESOLVED 2026-08-16 (business decision): accept as-is. Cairn OBS's own use (internal Kafka-protocol transport, no resale of broker access) is within BSL's Additional Use Grant; the third-party self-hosting-as-a-service question was judged unlikely to trip BSL's Streaming-or-Queuing-Service restriction, and was accepted as a known, disclosed risk rather than swapping to a heavier broker or dropping the bundled image. See license-audit-report.md's Redpanda section.",
|
||||
"classification": "c"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ docker compose up -d --build
|
||||
```
|
||||
|
||||
This builds and starts, in dependency order: `redpanda` → `redpanda-provision`
|
||||
(creates the `sentry.logs.raw` topic, then exits) → `clickhouse` →
|
||||
(creates the `cairnobs.logs.raw` topic, then exits) → `clickhouse` →
|
||||
`clickhouse-migrate` (applies `/storage/migrations`, then exits) →
|
||||
`ingest` and `api` → `web`.
|
||||
|
||||
@@ -133,7 +133,7 @@ In another terminal, **after** the agent is running and connected
|
||||
started won't be picked up):
|
||||
|
||||
```sh
|
||||
logger "hello from sentry phase 0"
|
||||
logger "hello from cairnobs phase 0"
|
||||
```
|
||||
|
||||
`logger` (part of util-linux, present on virtually every Linux distro)
|
||||
@@ -150,7 +150,7 @@ interval by default, so the line won't hit ingest instantly.
|
||||
starts every service in the file). Open `http://localhost:3000`, run the
|
||||
default query (`SELECT * FROM logs
|
||||
ORDER BY timestamp DESC LIMIT 100`), and look for a row with
|
||||
`message = "hello from sentry phase 0"`.
|
||||
`message = "hello from cairnobs phase 0"`.
|
||||
|
||||
**Or via curl, if you want to skip the browser:**
|
||||
|
||||
@@ -160,11 +160,11 @@ curl -X POST http://localhost:8080/query \
|
||||
-d '{"sql": "SELECT * FROM logs ORDER BY timestamp DESC LIMIT 10"}'
|
||||
```
|
||||
|
||||
**Or via sentryctl, just to confirm api is up (doesn't check the data
|
||||
**Or via cairnobsctl, just to confirm api is up (doesn't check the data
|
||||
itself):**
|
||||
|
||||
```sh
|
||||
cd cli && go run ./cmd/sentryctl ping
|
||||
cd cli && go run ./cmd/cairnobsctl ping
|
||||
```
|
||||
|
||||
If you see the row: that's Phase 0 done, end to end. If you don't, see
|
||||
@@ -199,7 +199,7 @@ Check each hop in order rather than guessing:
|
||||
at-least-once design (see `/ingest/README.md`), so this may just need
|
||||
more time rather than intervention.
|
||||
3. `docker compose exec redpanda rpk topic list` — confirm
|
||||
`sentry.logs.raw` exists (if `redpanda-provision` failed, it won't).
|
||||
`cairnobs.logs.raw` exists (if `redpanda-provision` failed, it won't).
|
||||
|
||||
**`docker compose up` fails on `service_completed_successfully`.**
|
||||
You're likely on Compose v1 (`docker-compose`, hyphenated) rather than v2
|
||||
|
||||
@@ -54,7 +54,7 @@ docker compose logs search
|
||||
```
|
||||
|
||||
You should see "search gRPC server listening" and rskafka connecting to
|
||||
all of `sentry.logs.raw`'s partitions. If you see nothing at all, check
|
||||
all of `cairnobs.logs.raw`'s partitions. If you see nothing at all, check
|
||||
`RUST_LOG=info` is set on the `search` service in `docker-compose.yml`.
|
||||
|
||||
### A2. Generate a log line and confirm both query paths agree
|
||||
|
||||
+10
-10
@@ -43,10 +43,10 @@ go run . --count 500000
|
||||
|
||||
Create a dashboard and a couple of panels, either through the web UI
|
||||
(`http://localhost:3000/dashboards` → "+ Create" → "+ Add panel") or via
|
||||
`sentryctl`:
|
||||
`cairnobsctl`:
|
||||
|
||||
```sh
|
||||
sentryctl dashboards apply my-dashboard.json # shape = GET /dashboards/{id}/export
|
||||
cairnobsctl dashboards apply my-dashboard.json # shape = GET /dashboards/{id}/export
|
||||
```
|
||||
|
||||
**Verified live**: a table panel (`severity=INFO | head 10`) and a bar
|
||||
@@ -95,7 +95,7 @@ Bring up a local webhook receiver for testing (no real Slack/PagerDuty
|
||||
needed):
|
||||
|
||||
```sh
|
||||
docker run -d --name sentry-webhook-sink --network sentry_default \
|
||||
docker run -d --name cairnobs-webhook-sink --network sentry_default \
|
||||
-p 9099:9099 -v $(pwd)/hack/webhook-sink:/src -w /src golang:1.25-alpine go run .
|
||||
```
|
||||
|
||||
@@ -104,7 +104,7 @@ Create a notification target and a rule, either via the web UI
|
||||
|
||||
```sh
|
||||
curl -X POST http://localhost:8081/targets -H 'Content-Type: application/json' -d '{
|
||||
"name": "local sink", "kind": "webhook", "webhook_url": "http://sentry-webhook-sink:9099/"
|
||||
"name": "local sink", "kind": "webhook", "webhook_url": "http://cairnobs-webhook-sink:9099/"
|
||||
}'
|
||||
|
||||
curl -X POST http://localhost:8081/rules -H 'Content-Type: application/json' -d '{
|
||||
@@ -119,7 +119,7 @@ curl -X POST http://localhost:8081/rules -H 'Content-Type: application/json' -d
|
||||
rule via the `/alerts/new` form, watched it transition in the browser):
|
||||
the rule transitions `ok` → `firing` on its first evaluation
|
||||
(`for_minutes: 0`), the delivery log shows `firing / sent / 200`, and
|
||||
`docker logs sentry-webhook-sink` shows the real received payload.
|
||||
`docker logs cairnobs-webhook-sink` shows the real received payload.
|
||||
|
||||
Also verified live: a threshold rule whose query returns **zero rows**
|
||||
records `last_eval_status: "error"` with the exact expected message
|
||||
@@ -205,13 +205,13 @@ already claimed. Moving off a single-process ticker to a distributed
|
||||
scheduler, and materially larger rule counts (10,000+), are both
|
||||
explicitly out of scope for this phase.
|
||||
|
||||
## 5. Confirm `sentryctl`
|
||||
## 5. Confirm `cairnobsctl`
|
||||
|
||||
```sh
|
||||
sentryctl dashboards list
|
||||
sentryctl dashboards apply exported-dashboard.json
|
||||
sentryctl alerts list
|
||||
sentryctl alerts apply rule.json
|
||||
cairnobsctl dashboards list
|
||||
cairnobsctl dashboards apply exported-dashboard.json
|
||||
cairnobsctl alerts list
|
||||
cairnobsctl alerts apply rule.json
|
||||
```
|
||||
|
||||
Both `dashboards` and `alerts` hit the exact same REST endpoints the web
|
||||
|
||||
@@ -108,7 +108,7 @@ yet. Stated plainly so this doesn't read as more built than it is.
|
||||
|
||||
## Schema
|
||||
|
||||
Lives in `/metadata` (`sentry_metadata`), alongside everything else from
|
||||
Lives in `/metadata` (`cairnobs_metadata`), alongside everything else from
|
||||
Phase 3, per `/docs/phase-4-isolation-design.md`'s existing schema
|
||||
additions (`tenants`, the `tenant_id` backfill on `alert_state`/
|
||||
`delivery_log`). New tables, continuing that migration sequence:
|
||||
|
||||
@@ -265,7 +265,7 @@ with three levels (`ok`/`warn`/`reject`) and human-readable reasons:
|
||||
query"), decided here rather than left ambiguous: hand-written queries
|
||||
get the identical assessment an AI-generated one would, so there's
|
||||
real parity, but retroactively hard-blocking existing dashboard/
|
||||
`sentryctl` query patterns that happen to have no time bound is a
|
||||
`cairnobsctl` query patterns that happen to have no time bound is a
|
||||
behavioral change this phase didn't set out to make and could break
|
||||
real existing usage. `warnings` is `omitempty` -- a client that
|
||||
doesn't look for it sees no shape change at all. All existing
|
||||
@@ -472,7 +472,7 @@ renders them is structurally the same conditional-message pattern
|
||||
already live-verified repeatedly for Explain/Fix/Optimize's own
|
||||
"unavailable" states, not new untested UI shape.
|
||||
|
||||
CLI (task 11): `sentryctl query --nl "..."` in `cli/cmd/sentryctl/cmd_query.go`.
|
||||
CLI (task 11): `cairnobsctl query --nl "..."` in `cli/cmd/cairnobsctl/cmd_query.go`.
|
||||
Same posture as the UI, enforced identically regardless of how the
|
||||
result was produced: a low-confidence, non-compiling, or cost-guard-blocked
|
||||
translation is never run, even with `--execute` -- confirmed by
|
||||
@@ -559,7 +559,7 @@ query at the moment of acceptance, not tracked keystroke-by-keystroke.
|
||||
|
||||
**Genuinely verified against a live Postgres**, not just unit-tested
|
||||
against a fake `InteractionLogger`: `metadata/migrations/0036` was
|
||||
applied to the running dev stack's `sentry-metadata-postgres`
|
||||
applied to the running dev stack's `cairnobs-metadata-postgres`
|
||||
(`docker compose up -d --build metadata-migrate`, confirmed via `\d+
|
||||
audit_log` before/after showing `ai_interaction` added to the
|
||||
`event_type` CHECK constraint), and two new tests in
|
||||
@@ -622,7 +622,7 @@ ends up with:
|
||||
handler/routing logic via `fakeProvider` -- fast, deterministic, no
|
||||
network, all run in CI today.
|
||||
2. **Integration tests** (this task, new): the mock-Ollama-server suite
|
||||
above, plus `cli/cmd/sentryctl/cmd_query_test.go`'s existing
|
||||
above, plus `cli/cmd/cairnobsctl/cmd_query_test.go`'s existing
|
||||
`httptest.Server`-backed coverage of `--nl`/`--execute` (already
|
||||
written during Track B, task 11) -- proves the plumbing (HTTP routing,
|
||||
JSON contracts, `planner.Compile`/`costguard.Assess` integration,
|
||||
|
||||
@@ -12,7 +12,7 @@ document is verification only.
|
||||
Every AI operation (`complete`, `explain`, `fix`, `optimize`,
|
||||
`translate`, and the audit-logging endpoint behind it) has been run
|
||||
end-to-end against a real `docker compose` stack — real HTTP requests
|
||||
into the real `sentry-api` container, through the real
|
||||
into the real `cairnobs-api` container, through the real
|
||||
`api/ai/provider/ollama.Client`, over a real network call, into a real
|
||||
process answering Ollama's actual `/api/chat` wire contract. **No real
|
||||
model weights are used anywhere in this verification** — see
|
||||
@@ -74,7 +74,7 @@ Run it as a container on the compose network with a network alias of
|
||||
`OLLAMA_BASE_URL` at it via a throwaway compose override:
|
||||
|
||||
```sh
|
||||
docker run -d --rm --name sentry-mock-ollama --network sentry_default --network-alias ollama \
|
||||
docker run -d --rm --name cairnobs-mock-ollama --network sentry_default --network-alias ollama \
|
||||
-v "$(pwd)/hack/mock-ollama:/src" -w /src golang:1.25-alpine \
|
||||
sh -c "go build -o /tmp/mock-ollama . && /tmp/mock-ollama"
|
||||
|
||||
@@ -106,7 +106,7 @@ wired into a stack anyone else might reach:
|
||||
|
||||
```sh
|
||||
docker compose up -d api # drops back to the plain env, no -f override
|
||||
docker rm -f sentry-mock-ollama
|
||||
docker rm -f cairnobs-mock-ollama
|
||||
rm /tmp/docker-compose.ai-verify.yml
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:8080/ai/translate -d '{}'
|
||||
# 404 -- confirms AI routes are unregistered again
|
||||
@@ -118,7 +118,7 @@ With AI routes enabled (step 2) and the web dev server running against
|
||||
`localhost:8080`, open the Search page's query bar:
|
||||
|
||||
- Type a partial query and pause — ghost text should appear inline
|
||||
after ~300ms; Tab accepts it. Stop `sentry-mock-ollama` and confirm
|
||||
after ~300ms; Tab accepts it. Stop `cairnobs-mock-ollama` and confirm
|
||||
ghost text just silently stops appearing (no error toast, no
|
||||
console noise) — this is the "graceful degradation" requirement,
|
||||
not incidental behavior.
|
||||
@@ -149,7 +149,7 @@ separately click "Run query".
|
||||
CLI:
|
||||
|
||||
```sh
|
||||
cd cli && go run ./cmd/sentryctl query --nl "errors in the last hour" --api http://localhost:8080
|
||||
cd cli && go run ./cmd/cairnobsctl query --nl "errors in the last hour" --api http://localhost:8080
|
||||
# prints the translated query and, in an interactive terminal, prompts y/N before running
|
||||
```
|
||||
|
||||
@@ -164,7 +164,7 @@ pattern applied to that service instead:
|
||||
1. Accept or dismiss a Fix/Optimize/Translate suggestion in the web UI.
|
||||
2. Confirm a row landed in `audit_log`:
|
||||
```sh
|
||||
docker exec sentry-metadata-postgres psql -U sentry -d sentry_metadata \
|
||||
docker exec cairnobs-metadata-postgres psql -U cairnobs -d cairnobs_metadata \
|
||||
-c "SELECT event_type, query_text, detail FROM audit_log WHERE event_type='ai_interaction' ORDER BY id DESC LIMIT 5;"
|
||||
```
|
||||
`detail` should show `operation`/`accepted`/`edited` matching what you
|
||||
|
||||
@@ -23,8 +23,15 @@ new cross-backend join strategy from scratch.
|
||||
Pipe syntax, SPL-inspired, EBNF-ish:
|
||||
|
||||
```
|
||||
query := base_search ("|" pipe_stage)*
|
||||
query := (base_search | pipe_stage) ("|" pipe_stage)*
|
||||
base_search := bool_expr // implicit filter/search, SPL convention
|
||||
// omitted entirely when the query starts
|
||||
// directly with a pipe-stage keyword (e.g.
|
||||
// `stats count by host`, no leading filter,
|
||||
// no leading "|") -- means match-everything.
|
||||
// A field genuinely named "where"/"stats"/etc
|
||||
// still parses as a filter (`where=foo`),
|
||||
// disambiguated by comparator lookahead.
|
||||
pipe_stage := "where" bool_expr
|
||||
| "stats" agg_call ("," agg_call)* ["by" field ("," field)*]
|
||||
| "sort" sort_field ("," sort_field)*
|
||||
|
||||
@@ -97,13 +97,13 @@ wire-level `SearchRequest` carries the right `tenant_id`. All pass, for
|
||||
real, no disclaimer needed for this specific claim.
|
||||
|
||||
**Both Helm and docker-compose now close this.**
|
||||
`deploy/helm/sentry/templates/api.yaml` and `enterprise-api.yaml` are
|
||||
`deploy/helm/cairnobs/templates/api.yaml` and `enterprise-api.yaml` are
|
||||
mutually exclusive, gated on opposite sides of the same
|
||||
`enterprise.enabled` flag, rendering to the same Service name/port — so
|
||||
a Helm-deployed cluster runs exactly one of the two binaries, chosen by
|
||||
the same flag that turns on RBAC/audit/SSO, not a second
|
||||
independently-forgettable decision. Verified by parsing (not
|
||||
eyeballing) the rendered YAML under both values: exactly one `sentry-api`
|
||||
eyeballing) the rendered YAML under both values: exactly one `cairnobs-api`
|
||||
Deployment either way, with the right image. `docker-compose.yml`'s
|
||||
`api`/`enterprise-api` services are now the analogous mutually-exclusive
|
||||
choice, gated behind `COMPOSE_PROFILES` (`.env` checks in
|
||||
@@ -233,7 +233,7 @@ Browser ──▶ enterprise-auth (GET /auth/oidc/login, /auth/oidc/callback)
|
||||
└─▶ external IdP (OIDC authorization code flow)
|
||||
└─▶ Postgres (rbacstore: users, tenant_memberships)
|
||||
|
||||
sentryctl ──▶ api, alerting (Bearer token when SENTRYCTL_TOKEN is set)
|
||||
cairnobsctl ──▶ api, alerting (Bearer token when CAIRNOBSCTL_TOKEN is set)
|
||||
```
|
||||
|
||||
Ingest path (agent → Redpanda → ingest → ClickHouse, and Redpanda →
|
||||
@@ -442,12 +442,12 @@ layer":
|
||||
|
||||
1. A dedicated `audit_writer` Postgres role with only `INSERT`+`SELECT`
|
||||
grants (`metadata/migrations/0012-0014`), via its **own**
|
||||
`pgxpool.Pool` — never the shared `sentry` role/pool every other
|
||||
`pgxpool.Pool` — never the shared `cairnobs` role/pool every other
|
||||
store uses.
|
||||
2. A `BEFORE UPDATE OR DELETE ... RAISE EXCEPTION` trigger
|
||||
(`metadata/migrations/0015-0016`) that rejects the operation for
|
||||
*any* role, including the table owner — confirmed live: even the
|
||||
`sentry` role cannot `UPDATE` a row without first disabling the
|
||||
`cairnobs` role cannot `UPDATE` a row without first disabling the
|
||||
trigger, a privileged operation distinct from ordinary application
|
||||
access.
|
||||
|
||||
@@ -547,10 +547,10 @@ terms:
|
||||
cluster/trusted network (`api`/`alerting`/`web`), never exposed
|
||||
publicly. Nothing in this codebase enforces that at the network layer;
|
||||
it's a deployment responsibility (NetworkPolicy, or equivalent) not
|
||||
yet codified in `/deploy/helm/sentry`.
|
||||
yet codified in `/deploy/helm/cairnobs`.
|
||||
- `ENTERPRISE_SESSION_SIGNING_KEY`, ClickHouse/Postgres passwords, and
|
||||
(once minted) the `alerting` service token are all K8s `Secret`
|
||||
objects in the Helm chart (`/deploy/helm/sentry/templates/
|
||||
objects in the Helm chart (`/deploy/helm/cairnobs/templates/
|
||||
secrets.yaml`) — standard K8s `Secret` semantics apply (base64, not
|
||||
encrypted at rest without a cluster-level `EncryptionConfiguration`).
|
||||
No secrets-manager integration (Vault, cloud KMS) exists; the chart
|
||||
@@ -571,7 +571,7 @@ terms:
|
||||
| Ingest tenant *identity* (credential validation, tagging) | **Built and tested** — fail-closed `TenantResolver`, `tenant_id` Kafka header attached per record |
|
||||
| Ingest tenant *write-routing*, ClickHouse | **Enforced, verified live** — `enterprise-ingest`/`chwriter.Registry` route each tagged batch to its tenant's own database, fail-closed on an untagged/unprovisioned tenant; both Docker-free and live-ClickHouse tests pass. Active-tenant snapshot refreshes every minute (`Registry.StartRefreshing`) — a deprovisioned tenant loses write access within a minute, not "until the next restart" |
|
||||
| Ingest tenant *write-routing*, Tantivy | **Built and genuinely verified** — `search/src/consumer.rs` routes each record into its own tenant's index via `IndexRegistry`, same registry the (already-verified) read side uses; no Docker needed, real tests pass. Active-tenant-gated too: `tenants::ActiveTenantTracker` polls `enterprise-auth` every 60s (off unless configured), refusing any tenant not in the polled allowlist — same one-minute staleness bound as ClickHouse's now-refreshing snapshot, no more asymmetry between the two |
|
||||
| Deployment actually routing traffic to `enterprise-api` (Helm) | **Enforced, verified live** — `api`/`enterprise-api` are mutually exclusive, same flag as RBAC/audit/SSO; a real `helm install` against a real `kind` cluster confirmed the `sentry-api` Deployment runs `sentry-enterprise-api:latest` with `enterprise.enabled=true`, real endpoints behind the `sentry-api` Service, not just `helm template`'s rendered YAML |
|
||||
| Deployment actually routing traffic to `enterprise-api` (Helm) | **Enforced, verified live** — `api`/`enterprise-api` are mutually exclusive, same flag as RBAC/audit/SSO; a real `helm install` against a real `kind` cluster confirmed the `cairnobs-api` Deployment runs `cairnobs-enterprise-api:latest` with `enterprise.enabled=true`, real endpoints behind the `cairnobs-api` Service, not just `helm template`'s rendered YAML |
|
||||
| Deployment actually routing traffic to `enterprise-api` (docker-compose) | **Enforced, verified live** — `api`/`enterprise-api` are mutually exclusive via `COMPOSE_PROFILES`, same flag choice as Helm's `enterprise.enabled`; a real `docker compose up` of `enterprise-api` was run in this environment (and caught/fixed a startup-crashing duplicate `GET /healthz` route registration bug in the process), not just `docker compose config` |
|
||||
| Human SSO login — OIDC | **Enforced, verified live** — real login against a real Auth0 developer tenant, full browser round trip; correctly failed closed on an identity with no `tenant_memberships` row, then succeeded and issued a real session after `-grant-membership-*`, with `POST /internal/authorize` returning exactly the granted tenant/role |
|
||||
| Human SSO login — SAML | **Enforced, verified live** — real login against Auth0's SAML2 Web App addon acting as a real SAML IdP, over real (self-signed, dev-only) TLS; a real signed assertion validated (audience, destination, signature), landed on `/select-tenant` with real memberships, and `POST /internal/authorize` confirmed the selected tenant/role. Found and fixed a real bug in the process: `loginhandler.go`'s cookies decided `Secure` from `r.TLS != nil` alone, which is wrong behind any TLS-terminating reverse proxy (the deployment shape this handler actually runs in) — `enterprise-auth` never terminates TLS itself, so `r.TLS` was nil even over a genuinely HTTPS connection, silently dropping `Secure` and breaking SAML's `SameSite=None` cookie |
|
||||
|
||||
Reference in New Issue
Block a user