Skip to content

AE control auth + CodeRabbit #53 followup#68

Open
entlein wants to merge 61 commits into
mainfrom
ae-followup-auth
Open

AE control auth + CodeRabbit #53 followup#68
entlein wants to merge 61 commits into
mainfrom
ae-followup-auth

Conversation

@entlein

@entlein entlein commented Jun 21, 2026

Copy link
Copy Markdown

Summary: AE control surface gains bearer-JWT auth + TLS, the trigger gets a PollLimit-saturation escape, fork-cloud config restored after the main merge, and CodeRabbit Go-code findings on the umbrella PR #53 are all addressed (controller rollback, NaN-finite gate, MaxBytesReader, streaming retain-on-fail, etc.).

Test Plan: build + 14/14 AE go test packages green locally on cb81ecd / c2642da; CI-image lint (tools/linters/lint_like_ci.sh) reports 0 Errors 0 Warnings; vizier-release built aeprod22 (cb81ecd) successfully, aeprod23 (c2642da) in flight; trigger differential oracle added (oracle_test.go) pins watermark advancement + boundary dedup + PollLimit-at-capacity + the documented overflow trade-off.

Type of change: /kind feature

AE control auth + CodeRabbit #53 followup

Followup to #53 (merge #53 first — this stacks on ae-prod; I'll retarget to main once #53 lands). Captured all 48 inline + 8 review + 12 issue CodeRabbit comments to biz/PoC/log4j/ae_pr53_coderabbit_feedback/ (JSON + DIGEST.md) so nothing is lost.

✅ Done in this PR

  • Bearer-JWT auth on the control surface — uses the same shared lib the broker/PEM use (px.dev/pixie/src/shared/services/utils); SetAuth verifies via jwtutils.ParseToken (signature + audience), middleware on Handler(), /healthz exempt. dx already mints this exact service JWT — it just attaches it. No new secret/crypto. Flag-gated (CONTROL_REQUIRE_AUTH, default off) for safe incremental rollout. Unit test mints a real JWT and asserts 401/pass/healthz. (CodeRabbit server.go: protect control endpoints.)
  • Input validation — reject invalid t_end (≤0) and query windows (start≥end, non-positive). (server.go: reject invalid t_end/window.)

Companion change (separate, entlein/dx)

dx aeclient.go must attach Authorization: Bearer <minted JWT> on its AE control POSTs. Then flip CONTROL_REQUIRE_AUTH=true + mount PL_JWT_SIGNING_KEY on AE. (Tracked; small.)

Already resolved on ae-prod (verified — no action)

  • http.Server timeouts (main.go) — already added (CodeRabbit r3379377432).
  • ~9 harness Majors (ae_config/ae_vs_all/deploy_ae/exp_datavolume_extreme/exp_log4j_reps/… fail-fast) — those scripts were deleted in the harness consolidation; moot.
  • The http_events=0 "bug" — self-retracted by the author (misdiagnosis).

Remaining AE-code Majors — triaged (to address as commits here)

Safe / clear:

  • streaming/filter.go:196 — timer leak on deltaCh close.
  • passthrough/passthrough.go:147 — per-table query/write timeouts.
  • pixieapi/pixieapi.go:89 — direct-mode TLS-skip uses wrong option (→ WithDirectTLSSkipVerify).
  • stats.py:57 — guard false "EXACT" when zero PASS rows.
  • schema.sql:485 — retention on ae_reconcile (unbounded growth).
  • controller.go:347 — don't fan out Pixie rows when attribution persistence fails.

Risky / needs careful testing before a presentation — defer with care:

  • controller.go:255 — rehydrated active windows never restart fan-out (state-machine path).
  • trigger/clickhouse.go:303 — inclusive-watermark paging (this is the F8 watermark area — change only with the F8 fixtures).
  • schema.sql:430adaptive_attribution ORDER BY key width (schema change — migration risk).
  • sink/clickhouse.go:433Record blocks hot path on CH (async refactor — perf-sensitive).

Entlein and others added 30 commits June 21, 2026 22:49
Signed-off-by: entlein <einentlein@gmail.com>
New env-gated background loop that runs the same PxL shape AE's
anomaly-gated path uses, but with an empty Target (no ns/pod predicate)
and over a configurable rolling window. Writes via the existing sink so
the byte-shape of forensic_db rows is comparable between the
PASSTHROUGH=1 phase (EVERYTHING) and the PASSTHROUGH=0 phase
(AE-FILTER). One-shot A/B that yields the per-table capture fraction of
the adaptive write path.

- internal/passthrough/passthrough.go — Loop + Config; defaults to
  30s window / 30s refresh / clickhouse.PixieTables() table list.
- internal/passthrough/passthrough_test.go — 6 tests; the load-bearing
  one is TestLoop_EmitsEmptyTargetPxL (asserts neither df.namespace nor
  df.pod predicates appear in the emitted PxL).
- cmd/main.go: ADAPTIVE_PASSTHROUGH + _WINDOW_SEC + _REFRESH_SEC env
  knobs. Adapter is constructed unconditionally when passthrough is on
  (joins the existing PushPixie / streaming construction path so the
  same pxapi grpc stream is reused). Loop is registered with the
  shutdown WaitGroup so SIGTERM waits for the in-flight tick.
- cmd/BUILD.bazel: drop @px// load (other AE BUILD.bazel files use
  //bazel — sticking out as the only one with @px is a leftover from a
  prior gazelle run; align). Add passthrough dep.

Signed-off-by: entlein <einentlein@gmail.com>
Stand-alone workflow that builds entlein/dx (private Active-Diagnosis
Framework) into ghcr.io/k8sstormcenter/dx-daemon. Separates the dx image
publish from the bazel-based vizier_release pipeline; the dx repo ships
its own Dockerfile.dxd (Go cross-compile + distroless final stage) so it
doesn't need to live as a submodule inside src/vizier/services/dx.

Triggers:
- tag push 'release/dx/v*' on this repo cuts a release build, image tag
  derived from the tag suffix (release/dx/v0.1.0 -> image tag 0.1.0).
- workflow_dispatch lets us build any dx ref on demand with a custom tag
  (default: short sha of the resolved dx commit).

Pulls dx via DX_ENTLEIN_PAT (already configured on the repo). Multi-arch
build (linux/amd64, linux/arm64); Dockerfile.dxd cross-compiles in the
native BUILDPLATFORM stage and the final stage is COPY-only, so target
emulation isn't required.

Signed-off-by: entlein <einentlein@gmail.com>
The dx image build pipeline lives in entlein/dx itself (PR #53,
branch feat/bazel-release): bazel-based with @px external pin to
pixie's ae-prod tip, pushes to docker.io/entlein/dx-daemon on a
release/dx/v* tag in the dx repo. The pixie-side buildx workflow
this reverts duplicated that intent in the wrong repo + the wrong
build system (docker buildx instead of bazel + pl_go_image macros)
+ the wrong registry (ghcr.io/k8sstormcenter instead of
docker.io/entlein).

Signed-off-by: entlein <einentlein@gmail.com>
…affordances

Fixes the silent-halt bug: the trigger gated on a RAW event_time high-water-mark,
so a single anomaly in a larger unit (ms/ns) drove the watermark past all real
seconds rows and AE stopped processing forever (data still on Pixie). Normalize
event_time to canonical nanoseconds in the poll SELECT filter+order and in the
in-memory/persisted cursor, boundary-dedup, and maxSeen (normalizeEventTimeNanos
+ chNormEventTimeNanos). Validated at the data layer: vs a poisoned watermark the
raw filter returns 0 rows, the normalized filter recovers all 60.

Also adds ADAPTIVE_PUSH_REFRESH_SEC (negative = single-shot pull) for the
reproducible load-test harness, an in-package trigger unit test, and an e2e
hermetic load test (mock PixieQuerier, exact rows+bytes).

Signed-off-by: entlein <einentlein@gmail.com>
…ness

Consolidate the adaptive_export load-test harness under src/e2e_test/ (matching
vzconn_loadtest / px_cluster conventions). Control-plane experiments (E1-E4, E6,
E8 sustained) are proven exactly-reproducible on a live rig; the data-plane
experiments (E5, E8 data-mode) are authored and pending live validation on a
vizier-registered rig (status documented in README + FINDINGS_AND_BACKLOG).

- harness/: shell + python (inject, exp_control, exp_e8, stats, ...) + lib helpers
- fixtures/EXPERIMENTS.md: curated kubescape_logs data-set catalog + expected outputs
- k8s/: isolated sinks + per-rep generator pod (no probes)
- tools/loadgen/: cleanloadgen + httpsink (docker-built test tool; .bazelignore'd
  pending a bazel target — lib/pq is already vendored in the module)
- FINDINGS_AND_BACKLOG.md: F8 watermark-poison bug + the fix + AE backlog

The AE Go unit/e2e tests live with the service (internal/{trigger,e2e}).

Signed-off-by: entlein <einentlein@gmail.com>
…C14) + diagrams

Signed-off-by: entlein <einentlein@gmail.com>
…iagram; gen sustained-DNS mode

C15 = AE must keep re-pulling+writing an active pod until t_end or DX stop (the
contract DX steers on; last week's 'wrote then stopped' is its violation). Add
DX-steering sequence diagram. Generator gains SUSTAIN_SEC (distinct-DNS trickle,
a Pixie-traced protocol) + configurable SETTLE_PRE_MS warm-up for fresh-pod
capture; harness wires GEN_SUSTAIN_SEC/GEN_SETTLE_MS.

Signed-off-by: entlein <einentlein@gmail.com>
…lisation

The 700821d trigger unit-normalisation wrapped event_time in a
multiIf(...) inside both the WHERE filter and the ORDER BY. Three
existing tests in watermark_test.go + one in clickhouse_test.go pinned
the raw 'event_time >= N' substring and broke at HEAD.

Update each test's expected substring to match the new normalized form
(') >= <ns-scaled N>' — the closing paren of multiIf, then the value
in canonical nanoseconds). Per-test ns-scaling:

  watermark_test.go:94  1744000000000000000  already ns -> unchanged
  watermark_test.go:125 InitialWatermark=42  < 1e10 sec -> * 1e9
  watermark_test.go:156 InitialWatermark=7   < 1e10 sec -> * 1e9
  watermark_test.go:297 event_time='5000'    < 1e10 sec -> * 1e9
  clickhouse_test.go:82 ref.T=1744..303e9 ns already ns -> unchanged

go test ./src/vizier/services/adaptive_export/... all green.

Signed-off-by: entlein <einentlein@gmail.com>
…future-stamp watermark poison)

Signed-off-by: entlein <einentlein@gmail.com>
Records one forensic_db.ae_reconcile row per data-plane pull (read_count vs
wrote_count, window, ns/pod) across ALL three write paths — controller fan-out
(filter), passthrough firehose, and streaming scanner — so a reconcile run
localizes loss to query (R5: read<PEM) vs sink (R6: wrote<read) and quantifies
re-pull dup (C8). Counts alone (write >= read) were proven insufficient.

- new internal/reconcile leaf package (Row, Recorder, Nop) — no import cycle
- sink.Record: CH-backed recorder (INSERT INTO forensic_db.ae_reconcile)
- ae_reconcile table: schema.sql + KnownTables + OperatorOwnedTables (synced);
  not a pixie table (absent from PixieTables, so VerifyPixieSchema ignores it)
- wired: passthrough.tick, controller.pushPixieRows (deferred, all return
  paths), streaming.scanner.Run; gated by ADAPTIVE_RECONCILE=true, else Nop
- unit test proves read/wrote capture incl. the sink-drop read>wrote shape
- fixed apply_test trailing-tables guard for the new operator table
- harness: exp_row_reconcile.sh (row-level PEM<->CH), ae_vs_all.sh, exp_datavolume_extreme.sh

Signed-off-by: entlein <einentlein@gmail.com>
px -o json empty result previously printed one blank line → counted as a phantom
LOSS=1. Guard: empty set → 0-byte keys file; drop all-empty-field keys. Confirmed
against the controlled log4j run (backend http 14/14 exact, conn 66>=12, no loss).

Signed-off-by: entlein <einentlein@gmail.com>
…log4shell

Reliable BY CONSTRUCTION against bob#140 (stateful/unreliable exploit on re-fire):
fresh-JVM backend (delete pod) + attacker-before-backend + the WORKING resolvable FQDN
attacker.<ns>.svc.cluster.local:1389 (NOT the bare attacker-ns.svc which NXDOMAINs and
gets dropped), then VERIFY the actual backend->:1389 LDAP egress in forensic_db.conn_stats
and RETRY until confirmed (the validity gate). Never assumes the exploit fired. Node-side.

Signed-off-by: entlein <einentlein@gmail.com>
…tion)

Reword from offensive 'exploit' to detection-signal-generation language: this validates
the kubescape->DX->AE detection chain. No logic change.

Signed-off-by: entlein <einentlein@gmail.com>
… http2

- pxl.CompilePassthrough/Render: precompile per-table PxL once (fixed
  window => constant relative start_time), only the two time_ bounds are
  stamped per tick. Rendered output is byte-identical to QueryFor with an
  empty Target (TestCompilePassthrough_MatchesQueryFor), so this is a
  structural change, not a capture change. upid->pod/ns stays in PxL.
- passthrough: tickConcurrent fans every table out at once (was a serial
  loop); shared pull() helper. Sink/recorder are pool/HTTP-backed and
  already called concurrently elsewhere.
- drop http2_messages.beta from the firehose set (not materialised on
  every cluster => ""Table not found"" every tick); shared PixieTables/DDL
  lists untouched.
- toggle ADAPTIVE_PASSTHROUGH_COMPILED (default on; =false reverts to the
  legacy serial QueryFor path).

Signed-off-by: entlein <einentlein@gmail.com>
…e.go

Fixes "missing strict dependencies: import of .../internal/reconcile" that
broke the AE image build for passthrough, sink, streaming, controller, cmd
(pre-existing since the ADAPTIVE_RECONCILE commit added the package + imports
without bazel deps; never CI-built). Also wires the new pxl/compile.go srcs
+ passthrough/pxl test srcs (compiled_test.go, reconcile_test.go, compile_test.go).

- new internal/reconcile/BUILD.bazel (go_library, stdlib-only)
- +//internal/reconcile dep: passthrough, sink, streaming, controller, cmd
- pxl go_library +compile.go; pxl_test +compile_test.go; passthrough_test +compiled_test.go,reconcile_test.go (+reconcile dep)

Signed-off-by: entlein <einentlein@gmail.com>
F1 RCA: Pixie caps every px.display at max_output_rows_per_table (default
10000, query_flags.go) — the planner add_limit_to_batch_result_sink_rule
silently truncates wide firehose windows / busy pods at the READ (write path
is clean: ae_reconcile shows read==wrote). Fix uses Pixie own native knob —
prepend `#px:set max_output_rows_per_table=1000000` to every generated PxL
(QueryFor + CompilePassthrough) so all pull paths are uncapped. Validated on
rig: a 14208-row window returned 10000 (capped) vs 14298 (with flag). No
pagination loop, no extra round-trips. See memory project-ae-passthrough-10k-cap.

Signed-off-by: entlein <einentlein@gmail.com>
Consolidates the recurring content_type silent-drop incident class
into one default-suite test gate (6 tests, ~15ms):

  I1 TestContract_ContentTypeIsInt64InSchema
  I2 TestContract_FastEncodeContentTypeAsInt
  I3 TestContract_SilentDropDetected
  I3.b TestContract_SilentDropNotTriggeredOnSuccess
  I3.c TestContract_SilentDropToleratesMissingSummaryHeader
  I4 TestContract_HTTPEventsRoundTrip

Top-of-file docstring chronicles the incident timeline so future
operators can grep their way to the contract.

Signed-off-by: entlein <einentlein@gmail.com>
Measures the AdaptiveExport value prop: datavolume REDUCTION of DX-steered AE
(rev-3 streaming, AE writes only DX-steered activeSet pods over the control
surface) vs saving ALL data (passthrough firehose). Two arms, same fixed load,
forensic_db active-part deltas (rows+bytes) per table; reduction = 1 - DX/ALL.
Uses the canonical resolvable JNDI FQDN (attacker.attacker-ns.svc.cluster.local)
so the chain fires + DX can classify (a malformed host → NXDOMAIN → no steer).
Successor to ae_vs_all.sh, whose AE arm used the rev-2 controller gate + stale JNDI.

Signed-off-by: entlein <einentlein@gmail.com>
Measures all AE non-functional requirements under steady load on the rig:
throughput (rows+bytes/sec), capture completeness (AE read vs broker count =
F1 cap proof), write fidelity (read==wrote + write-error count), end-to-end
freshness latency (now - max(time_) in CH), resource footprint (AE pod cpu/mem
idle vs loaded), per-cycle cadence. Emits a structured report; companion to
exp_dx_steering_reduction.sh. Real-data only.

Signed-off-by: entlein <einentlein@gmail.com>
…ring + live-pod guard)

Run-1 reported false 100% reduction because stale adaptive_attribution windows
rehydrated DEAD pods (deleted loaders) into the activeSet → AE streamed dead pods
→ 0 rows. Clear adaptive_attribution before the DX arm so the activeSet only gets
freshly-steered LIVE pods; add a guard that prints the steered pods + marshalsec
fire count + live log4j-poc pods so a dead-arm result is caught, not reported as
a reduction.

Signed-off-by: entlein <einentlein@gmail.com>
lag query used now()-DateTime64 (type error -> na); use dateDiff(second,...).
Capture-completeness vs broker was window-misaligned (623% artifact) -> drop it;
report tot_read vs tot_wrote (read==wrote) + errs instead. The 10k-cap/completeness
proof is the dedicated F1 test (max_read>10000 vs broker for the SAME window).

Signed-off-by: entlein <einentlein@gmail.com>
…ary)

Run-2 byte-delta reduction came out negative because system.parts byte delta is
compaction-noisy (merges land mid-window). Report rows reduction as primary
(actual captured-row count, noise-free); keep bytes as secondary context.

Signed-off-by: entlein <einentlein@gmail.com>
Eviction-RCA finding (PR #63 NFR campaign): AE had NO memory limit (only cpu
300m) and was CPU-pinned at 300m under concurrent passthrough. AE measured tiny
(16-38Mi steady), but the raised 1M-row passthrough cap can spike, so cap at 1Gi
so AE can never memory-pressure a node; raise cpu limit 300m->1 core (was
throttling). NOTE node evictions were NOT AE/OOM — node-01 went NotReady
(network/heartbeat); the memory consumer is PEM (1365Mi, OOMs at the 2Gi default).

Signed-off-by: entlein <einentlein@gmail.com>
Root cause of the recurring "AE unauthenticated / writes 0 / crashloop" reverts:
kustomization.yaml bundled adaptive_export_secrets.yaml (placeholder pixie-api-key)
with the role+deployment, so EVERY infra re-apply (make log4j) clobbered the real
key that ae-auth had written. Separation of concerns: remove the secret from the
kustomization — infra (role+deployment) stays re-appliable; the secret holds real
creds and is owned solely by `make ae-auth`, created once, never touched by infra
re-applies. Secret manifest kept as a hand-applied seed-only template (documented).

Signed-off-by: entlein <einentlein@gmail.com>
The DX-steered arm was failing because the harness only fired stage-1 (JNDI/LDAP).
That generates ldap-egress but NO kubescape R0001 → backend never flagged → DX no
case → indeterminate → AE steers wrong/no pods. R0001 comes from stage-2 (post-
exploitation exec). fire() now does stage-1 (JNDI) + stage-2 (whoami/shadow/token/
getent in the backend) → kubescape R0001+R0006 → DX rules backend MALIGNANT →
backend enters AE activeSet → reduction is measurable. Verified live: DX evidence
unexpected-spawn+sensitive-file-read → verdict ruled_in generic=MALIGNANT.

Signed-off-by: entlein <einentlein@gmail.com>
…dd DX-steering diagnostics

Standing terminology rule: allowlist/blocklist, never whitelist/blacklist.
Pure rename (no behavior change) of the rev-3 streaming filter:
  FilterModeWhitelist  → FilterModeAllowlist
  MaxWhitelistSize     → MaxAllowlistSize
  ADAPTIVE_STREAM_MAX_WHITELIST → ADAPTIVE_STREAM_MAX_ALLOWLIST  (env)
  mode=whitelist log string → mode=allowlist
plus all comments/identifiers/tests in streaming, activeset, cmd/main.

DX-steering diagnostics (the reason DX-arm-writes-0 has been hard to RCA —
we could not tell "empty ActiveSet" from "broker returned 0 rows"):
  - scanner: log the empty-allowlist short-circuit (was silent) so an
    empty ActiveSet is visible in logs, distinct from "query completed rows=0".
  - FilterUpdater: emitted-filter log Debug→Info so the steered pod count
    per ActiveSet change is visible without debug logging.

NOTE: ADAPTIVE_STREAM_MAX_WHITELIST env renamed → tooling that sets the old
name must switch to ADAPTIVE_STREAM_MAX_ALLOWLIST.

Signed-off-by: entlein <einentlein@gmail.com>
The Pixie dx_evidence_graph UI reads dx_attack_graph via px.DataFrame
clickhouse_dsn, whose query template hardcodes event_time + hostname and
ORDER BY event_time. A table without those columns fails 'Unknown
identifier event_time'; a table created by hand (local, not via the
operator) isn't globally registered. Fix: make AE own it like the other
forensic tables.

- schema.sql: dx_attack_graph DDL with event_time(UInt64 nanos) + hostname,
  edge columns, fromUnixTimestamp64Nano partition/TTL (nanos-correct).
- KnownTables + OperatorOwnedTables: register it so Apply creates it at boot.
- apply_test: assert last-applied DDL == last OperatorOwnedTables entry
  (robust to appended operator tables) instead of hardcoding trigger_watermark.

go test ./.../clickhouse green.

Signed-off-by: entlein <einentlein@gmail.com>
Pixie's clickhouse_dsn type mapper reads UInt8 as BOOLEAN and does not
handle UInt16/UInt32/Float32 -> px fails with 'Column[N] given incorrect
type' rendering the dx_evidence_graph. weight/max_severity/num_findings
-> Int64, confidence -> Float64 (map cleanly to INT64/FLOAT64). Verified
live: px run returns all 6 edges with every column. event_time stays
UInt64 (matches kubescape_logs, which px reads).

Signed-off-by: entlein <einentlein@gmail.com>
…canner buildPxL

The DX/streaming arm silently capped each per-table pull at Pixie's default
10000-row limit while the passthrough/ALL arm (pxl.CompilePassthrough /
QueryFor) already raises it to 1,000,000 via the broker's #px:set query flag.
Validated live on 6a33dac0: a single streaming http_events pull returned
exactly rows=10000 (the cap). Left unfixed this UNDER-counts the DX arm and
OVERSTATES the DX-vs-ALL volume reduction. Prepend the same #px:set directive
to the streaming scanner's PxL so both arms are uncapped and comparable.

Signed-off-by: entlein <einentlein@gmail.com>
@entlein

entlein commented Jun 22, 2026

Copy link
Copy Markdown
Author

there are indications that it causes PEM to crash, needs RCA

entlein and others added 2 commits June 23, 2026 18:28
Auth without TLS is half a control: tcpdump on dx→AE :9100 captured 720
cleartext `Authorization: Bearer` JWTs in 70s — the #68 token crosses the
CNI in plaintext. CONTROL_TLS=true now serves TLS with server.crt/key from
the service-tls-certs secret (broker/PEM already use it; dx skip-verifies).
Default-OFF for incremental rollout, symmetric to CONTROL_REQUIRE_AUTH.
Stacks on #68 (ae-followup-auth). dx client half: entlein/dx#88.

Co-authored-by: Entlein <eineintlein@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/vizier/services/adaptive_export/cmd/main.go
#	src/vizier/services/adaptive_export/internal/clickhouse/schema.sql
#	src/vizier/services/adaptive_export/internal/control/BUILD.bazel
#	src/vizier/services/adaptive_export/internal/control/server.go
#	src/vizier/services/adaptive_export/internal/control/server_test.go
#	src/vizier/services/adaptive_export/internal/controller/controller.go
#	src/vizier/services/adaptive_export/internal/passthrough/passthrough.go
#	src/vizier/services/adaptive_export/internal/trigger/clickhouse.go
#	src/vizier/services/adaptive_export/internal/trigger/clickhouse_internal_test.go
@entlein

entlein commented Jun 23, 2026

Copy link
Copy Markdown
Author

Merged main in — conflicts resolved (now MERGEABLE). main landed #53 (production AE), which this branch builds on (+auth +TLS +#53-followups +#67). 9 files conflicted; all resolved keeping this branch's followup work on top of #53 (main's side was the pre-followup #53 in every case):

file kept
cmd/main.go bearer-auth block + CONTROL_TLS/ListenAndServeTLS
clickhouse/schema.sql 30d TTL on the debug-log table
control/BUILD.bazel //src/shared/services/utils dep (auth)
control/server.go SetAuth + auth middleware + input validation
control/server_test.go TestControlAuth
controller/controller.go re-arm fan-out on restart + skip-fan-out on attribution-persist-fail (CodeRabbit)
passthrough/passthrough.go per-table context.WithTimeout (CodeRabbit)
trigger/clickhouse.go #67 PollLimit watermark boundary-escape
trigger/clickhouse_internal_test.go sync.Mutex race-fix on the test

Verified: 0 conflict markers, gofmt clean on all resolved .go, and signature coherence (control.New/SetAuth/jwtutils.ParseToken calls match defs). Bazel build is the CI gate (not run locally).

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

entlein added 2 commits June 24, 2026 07:46
… → 73.7% cov)

The trigger's incremental kubescape-events pump has 3 moving parts
that must agree: watermark advancement, boundary fingerprint dedup,
and PollLimit-saturated draining (PR #67 fix). Existing tests cover
each in isolation. This new file pins them TOGETHER against the
simplest possible reference ("drain everything in event_time order,
dedupe by fingerprint, advance the cursor to max(event_time)"). If
the iterative trigger and the reference disagree on the set of
emitted rows for ANY poll sequence, one of the three parts is wrong.

Four oracle tests:

  TestOracle_TriggerEmitsNaiveSet_StaggeredCorpus — 50 rows scattered
  across distinct event_times, PollLimit=10 forces ≥5 polls; trigger
  must emit exactly the 50 rows the naive reference computes.

  TestOracle_PollLimitSaturation_AtCapacity — regression guard for PR
  #67 (dfdc465): when EXACTLY PollLimit rows share a boundary
  event_time, every one of them must emit, and the cursor must clear
  the boundary for the next-event_time row that follows.

  TestOracle_PollLimitOverflow_DocumentsLossBound — pins PR #67's
  intentional trade-off: when >PollLimit rows share a boundary
  event_time, the first PollLimit emit, then the 1ns escape advances
  the cursor past the surplus. Lock-in test: if a future fix recovers
  the surplus (good!) this test fails loudly so both can update in
  lock-step instead of regressing silently.

  TestOracle_BoundaryDedup_NoDuplicates — when CH returns the same
  boundary row in two consecutive polls (real production case: a new
  row lands at the same event_time after a previous poll's cursor
  advanced past it), the seenAtBoundary map must filter the duplicate.

Coverage: trigger 71.6% → 73.7% (statement-level). The oracle's value
isn't in statement count — it's in property-level coverage on
invariants the per-feature tests can't enforce together.

Build.bazel: adds oracle_test.go to the existing trigger_test target.
…cle-vm-16cpu)

Merge from main re-introduced the deprecated 'oracle-16cpu-64gb-x86-64'
label on the Build Release job. That label doesn't resolve in the
fork's runner pool, so aeprod22's release workflow sat queued
indefinitely. update-gh-artifacts-manifest at L143 already had the
'-vm-' variant; align Build Release at L18 to match. Same fix the
fork applied earlier in 21d536e for the standalone vizier_release
workflow — main keeps regressing it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/vizier/services/adaptive_export/internal/trigger/oracle_test.go`:
- Around line 351-365: The overflow test has two issues: the timeout branch in
the receive loop only breaks the select, so the outer loop can spin forever, and
the error returned by tr.Subscribe(ctx) is being ignored. Update the Subscribe
setup in oracle_test.go to check the error and fail fast with t.Fatalf, and
change the deadline case in the collected-events loop to explicitly fail the
test or exit the loop so the timeout path cannot hang.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 50bd8567-0709-4cfd-b548-c297ff735a59

📥 Commits

Reviewing files that changed from the base of the PR and between 0c65751 and 0fd9c3f.

📒 Files selected for processing (3)
  • .github/workflows/vizier_release.yaml
  • src/vizier/services/adaptive_export/internal/trigger/BUILD.bazel
  • src/vizier/services/adaptive_export/internal/trigger/oracle_test.go

Comment thread src/vizier/services/adaptive_export/internal/trigger/oracle_test.go
entlein added 5 commits June 24, 2026 10:21
…i.bzl, fork cockpit)

The merge of main into ae-followup-auth (0c65751) silently took the
upstream version on a swath of files where the fork had previously
deviated. This commit reverts the merge's regressions back to the
fork-correct state (origin/ae-prod) AND completes the runner-label
sweep so every release/mirror/perf workflow uses the same -vm-16cpu
label the fork's runner pool actually has.

1. Runner labels — main re-introduced 'oracle-16cpu-64gb-x86-64' and
   'oracle-8cpu-32gb-x86-64' on 7 workflows. Fork's pool only resolves
   'oracle-vm-16cpu-64gb-x86-64'; both stale labels sit queued forever.
   Fixed: cli_release.yaml (L18+L212), cloud_release.yaml (L18),
   mirror_demos.yaml (L12), mirror_deps.yaml (L12),
   mirror_releases.yaml (L13), operator_release.yaml (L18+L143),
   perf_common.yaml (L37+L60). vizier_release.yaml L18 was fixed
   already in 0fd9c3f.

2. bazel/ui.bzl — main reverted PR #64's webpack-build fixes that
   broke release/cloud/v0.0.10 with 'export: `18': not a valid
   identifier'. Restored: 'set -x' for action-shell tracing, PATH
   that puts /opt/px_dev/tools/node/bin FIRST, 'hash -r', the
   STABLE_BUILD_TAG|BUILD_TIMESTAMP allowlist sed (vs the wildcard
   that word-splits FORMATTED_DATE), and use_default_shell_env=True
   so --incompatible_strict_action_env doesn't strip yarn from PATH.

3. 28 fork-cloud config files — main's PR pixie-io#2391 (cert-manager
   migration) deleted private/cockpit/*,
   terraform/kubernetes/auth0/*, terraform/kubernetes/cloud_deps/*,
   .sops.yaml, private/skaffold_cloud.yaml. These are still load-
   bearing for the AOCC pixie-cloud deployment; the fork hasn't
   migrated to cert-manager-compatible secrets yet (PR pixie-io#2391's
   monitor.go fallback path is in place, so adoption is the
   follow-up, not a blocker). Restored all 28 from origin/ae-prod.

Genuine main pickups that were CORRECT to keep (no fix needed): the
src/utils/shared/k8s/{apply,delete}.go import-order +
sets.New[string] generics migration,
src/operator/controllers/monitor.go's cert-manager secret fallback,
and the src/carnot/BUILD.bazel + src/carnot/exec/BUILD.bazel
additions.
11 TRUE + 2 PARTIAL CodeRabbit comments verified against current code;
all valid. This commit lands every one of them as a discrete change,
keeps the existing tests green, adds new tests where the contract
itself changed.

🔴 Real bugs:

  controller/controller.go: handle() now SNAPSHOTS c.active[hash]
    before mutation and ROLLS BACK on sink.Write failure. Without
    this, a failed persist left c.active extended; an already-running
    pushPixieRows would re-snapshot and fan out data based on an
    attribution row that never landed in CH. Updated
    TestController_SinkErrorNonFatal to pin the new rollback contract
    (active==0 after sink error) and added a writeAttempts() observer
    on fakeSink so the test doesn't race.

  sink/fastencode.go: appendJSONValue now rejects NaN/+Inf/-Inf
    floats with errFastEncodeUnsupported (triggers the encoding/json
    fallback). Previously strconv.AppendFloat emitted invalid JSON
    tokens that made CH reject the whole batch.

  streaming/writer.go: flush() keeps the row buffer on write failure
    (so the next attempt retries instead of silently dropping), and
    the shutdown branch uses context.Background() for the final flush
    so it isn't fast-failed by the already-cancelled parent ctx.

  control/server.go: decode() now wraps the body in
    http.MaxBytesReader(w, body, 4 MiB) so an oversized JSON payload
    on the public(-ish) control endpoint can't OOM the operator. The
    JWT auth still gates access — this hardens what a holding-an-
    acceptable-JWT attacker can do.

🟠 Lower-impact:

  cmd/main.go: isOperatorManagedScript matches the EXACT builtin
    names (no "ch-" prefix), so a user-authored script named
    "ch-something-custom" can't get deleted under
    INSTALL_PRESET_SCRIPTS=true.

  chhttp/chhttp.go: QueryStream now uses a separate http.Client with
    no Timeout. Go's http.Client.Timeout covers body reads, so
    reusing the 30s default would silently truncate a multi-MB
    active-set rehydrate. Stream callers must bound via ctx.Deadline.

  passthrough/passthrough.go: tickConcurrent's precompile-skip branch
    now calls l.rec(...) so the compiled and legacy paths produce
    identical reconcile-row counts. Previously the compiled path
    silently dropped a table → invisible divergence.

  trigger/oracle_test.go: COLLECT: labelled break stops the busy-spin
    on deadline expiry. Bare "break" only exits the select, leaving
    the for-loop to busy-spin until the count condition is reached.

🟡 Cosmetic / cleanup:

  control/server_test.go: TestBadInputRejected now pins t_end<=0 on
    /export/start AND inverted/zero window on /query — 4 new
    assertions for validators that existed but were untested.

  pixieapi/pixieapi.go: TODO comment marking the bounded pxapi.Client
    leak for follow-up when direct-mode throughput crosses ~1 q/s.

  sink/clickhouse.go: "sink: pixie write completed" demoted Info
    → Debug. One log per Pixie batch in fan-out paths was avoidable
    log-volume pressure; the silent-drop guard below still fires
    loud on the actual failure mode.

  sink/integration_test.go: per-table 15s ctx (was a shared 60s for
    the entire loop) so a slow early table can't starve later ones.

  trigger/clickhouse.go: PollLimit docstring now matches the
    0→10000 rewrite in New(); "unlimited" is no longer a documented
    option.

Local gates: build exit 0, go test 14/14 packages pass,
tools/linters/lint_like_ci.sh OKAY (0 Errors, 0 Warnings).
…lename-linter

The fork's filename-linter (inherited from upstream pixie-io) rejects
any PR diff that touches a path containing 'private'. The restored
fork-cloud config from the main-merge fix (cb81ecd) added 12
private/cockpit/* + 1 terraform/credentials/cockpit/* files, which
upstream-main had deleted via PR pixie-io#2391 — the linter then correctly
saw them as 'new private/* additions' and failed.

These specific paths are LEGITIMATE fork-cloud deployment config that
pre-dates the upstream linter; the original 'no private/* leaks'
intent stays in place for everything else via two negative-glob
exceptions.
…ccepts the fork-cloud config upstream

Honest fix for the merge-regression: the fork's filename-linter rejects
any PR diff touching a path containing 'private' (an upstream-pixie-io
guard against secrets leaking into OSS PRs). The fork is a CONTRIBUTOR
that upstreams, so the guard applies; the prior 'restore from ae-prod'
fix legitimately re-added private/cockpit/* (which upstream-main pixie-io#2391
deleted via cert-manager migration) but tripped the linter.

Reverts the lazy carve-out fix (c579e48 reverted 7f43c51) and does
the structural rename instead.

Moves:
  private/cockpit/                  -> cockpit/
  private/skaffold_cloud.yaml       -> skaffold/skaffold_cloud.yaml
                                       (collapsed with the upstream-
                                       resurrected skaffold/skaffold_cloud.yaml
                                       — the fork's private/ version was
                                       the maintained superset)

Reference updates:
  cockpit/kustomization.yaml  ../../k8s/cloud/...  ->  ../k8s/cloud/...
                                       (depth-1 after the rename)
  skaffold/skaffold_cloud.yaml:84  - private/cockpit  ->  - cockpit

Linter intent preserved: any future PR that touches a real 'private/*'
path still fails — the guard's correct semantics are restored.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/vizier/services/adaptive_export/internal/trigger/clickhouse.go (1)

322-338: 🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift

Do not advance past the boundary timestamp without a total row order.

Line 401 only orders by normalized event_time. That means a full page of already-seen rows at the current watermark does not prove there are no unseen rows with the same timestamp after the LIMIT. Bumping the watermark by +1ns here can therefore drop real rows permanently whenever more than PollLimit rows share the boundary timestamp.

This needs a stable tie-breaker in the query/state (for example, page by event_time plus a secondary key/fingerprint) instead of skipping to the next timestamp.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/trigger/clickhouse.go` around
lines 322 - 338, The paging escape in the ClickHouse trigger advances the
watermark past a boundary timestamp without a total row order, which can
permanently skip unseen rows. Update the polling logic in the trigger code path
around the boundary handling to avoid incrementing the watermark by 1ns when the
query is only ordered by normalized event_time. Instead, add a stable secondary
tie-breaker to the pagination/state in the query and cursor logic used by the
trigger so rows at the same timestamp can be paged deterministically without
dropping data.
src/vizier/services/adaptive_export/internal/chhttp/chhttp.go (1)

107-122: 🩺 Stability & Availability | 🟠 Major

Add a timeout to the boot-time Rehydrate call. cmd/main.go:443 passes the long-lived service context (context.WithCancel(context.Background())) into ctl.Rehydrate, so the new no-timeout QueryStream path can block startup indefinitely if ClickHouse stalls. Wrap that call in a deadline-bound context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/chhttp/chhttp.go` around lines
107 - 122, The boot-time ctl.Rehydrate call is using a long-lived service
context, so the new QueryStream path can block startup forever if ClickHouse
stalls. Update the startup flow around ctl.Rehydrate to create and pass a
deadline-bound context instead of the background-derived cancel-only context,
and ensure the timeout is scoped just to rehydration. Use the existing
ctl.Rehydrate call site and the QueryStream behavior in chhttp.Client to locate
the fix.
♻️ Duplicate comments (3)
src/vizier/services/adaptive_export/internal/trigger/oracle_test.go (1)

351-351: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check the Subscribe setup error.

Line 351 still discards the error from tr.Subscribe(ctx), so a setup failure gets reported as a later collection timeout instead of failing fast at the source.

Suggested fix
-	ch, _ := tr.Subscribe(ctx)
+	ch, err := tr.Subscribe(ctx)
+	if err != nil {
+		t.Fatalf("Subscribe: %v", err)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/trigger/oracle_test.go` at line
351, The test setup in `Subscribe` is still ignoring the returned error, which
can hide a subscription failure until a later timeout. Update the
`tr.Subscribe(ctx)` call in `oracle_test` to handle the error immediately and
fail the test at the source, using the existing `tr` and `Subscribe` flow rather
than continuing with a potentially nil channel.
src/vizier/services/adaptive_export/internal/controller/controller.go (1)

344-382: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not publish unpersisted attribution state before Sink.Write succeeds.

The rollback happens after the failed write, but Lines 344-353 already mutate c.active; an existing pushPixieRows loop can re-snapshot that unpersisted extension while Sink.Write is still pending and fan out rows against a phantom attribution anchor. Stage the updated row in a local copy, write it first, then commit it to c.active under the mutex only after persistence succeeds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/controller/controller.go` around
lines 344 - 382, The attribution update in controller logic is mutating c.active
before Sink.Write succeeds, which can let pushPixieRows observe and fan out an
unpersisted snapshot. Fix the flow in the controller method that handles the row
update by staging the modified AttributionRow in a local copy, calling
c.sink.Write first, and only committing the new state into c.active under c.mu
after the write succeeds; keep the rollback path for failures, including the
inFlight bookkeeping, unchanged.
src/vizier/services/adaptive_export/internal/control/server_test.go (1)

181-189: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add a non-positive window timestamp case.

These cases pin inverted and zero-length windows, but not the new Window[0] <= 0 || Window[1] <= 0 contract in handleQuery.

Proposed test addition
 	if r := do(t, srv, http.MethodPost, "/query",
 		`{"pod":"p","table":"http_events","query_id":"x","window":[5,5]}`); r.StatusCode != http.StatusBadRequest {
 		t.Fatalf("query zero-window = %d, want 400", r.StatusCode)
 	}
+	if r := do(t, srv, http.MethodPost, "/query",
+		`{"pod":"p","table":"http_events","query_id":"x","window":[0,5]}`); r.StatusCode != http.StatusBadRequest {
+		t.Fatalf("query non-positive-window = %d, want 400", r.StatusCode)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/control/server_test.go` around
lines 181 - 189, The query tests in server_test.go cover inverted and
zero-length windows, but they do not exercise the new non-positive timestamp
validation in handleQuery. Add a test case in the existing /query checks using
do(...) with a window where either Window[0] or Window[1] is non-positive, and
assert it returns http.StatusBadRequest; place it alongside the current window
validation cases so the handleQuery contract is pinned by the tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/mirror_demos.yaml:
- Line 12: The workflow job’s custom runner label was renamed to
oracle-vm-16cpu-64gb-x86-64, but actionlint will still fail until the shared
custom runner label list is updated to match. Update the actionlint
configuration that defines allowed self-hosted labels so it includes the new
label used by the workflow’s runs-on entry, keeping both references in sync.

In `@bazel/ui.bzl`:
- Around line 78-79: Guard the stamp export logic in the shell command built by
the `ui` rule so missing `STABLE_BUILD_TAG` or `BUILD_TIMESTAMP` entries do not
remove the leading command and break the `&&` chain before `yarn build_prod`.
Update the two `sed` expansions that reference `ctx.info_file.path` and
`ctx.version_file.path` to a no-op-safe form, such as wrapping each in `eval
"$(sed ...)"`, so the command still succeeds when a key is absent.

In `@cockpit/cloud_ingress.yaml`:
- Around line 7-16: The ingress route for cloud-proxy-service:443 defines the
backend transport but never applies it, so the serverName override is ignored.
Update cockpit/cloud_ingress.yaml to attach the Traefik service.serverstransport
annotation on the / backend route and point it to cloud-backend-transport, using
the existing ingress resource so the backend actually picks up the transport
settings.

In `@cockpit/plugin_db_updater_job.yaml`:
- Around line 15-90: Harden the Job spec for the updater workload by setting
restrictive pod/security defaults before it consumes PL_POSTGRES_PASSWORD and
PL_JWT_SIGNING_KEY. Update the Job template in plugin_db_updater_job.yaml to
disable root execution and privilege escalation, drop unnecessary Linux
capabilities, and prevent automatic service-account token mounting; apply these
settings at the pod-level and, if needed, reinforce them on the postgres-wait
and updater containers.

In `@cockpit/proxy_envoy.yaml`:
- Around line 43-50: The CORS configuration in the Envoy proxy is using a
suffix-based origin match, which is broader than the single approved host and
can match unintended subdomains. Update the cors allow_origin_string_match
setting to use an exact origin match for the same host configured in
cockpit/domain_config.yaml, and keep the rest of the cors settings unchanged so
credentialed requests are only allowed from that one origin.
- Around line 87-96: The UpstreamTlsContext for api_service only configures a
client certificate and does not verify the backend server identity. Update the
envoy transport socket configuration to add a validation_context with the
trusted CA for the upstream certificate chain and set the appropriate
sni/hostname so Envoy validates the server cert before proxying traffic. Keep
the fix localized to the UpstreamTlsContext/common_tls_context block so the
server authentication settings are applied alongside the existing
tls_certificates.

In `@src/vizier/services/adaptive_export/internal/control/server.go`:
- Around line 185-188: The decode helper currently accepts a valid first JSON
value and ignores any trailing bytes, so malformed control requests can slip
through. Update decode in server.go to use the existing json.Decoder on r.Body,
then verify there is no extra non-whitespace input remaining after the first
Decode before returning success. If any trailing junk is present, make the
request fail so the caller returns a 400.

In `@terraform/kubernetes/auth0/.terraform.lock.hcl`:
- Around line 4-6: The Auth0 provider is still allowed to resolve from a
too-broad version range, which can let lockfile regeneration pull in an untested
major. Update the required provider constraint in auth0_main.tf for auth0/auth0
to the validated series used by the lock entry, then regenerate the lockfile so
.terraform.lock.hcl stays aligned with that pinned range.

In `@terraform/kubernetes/auth0/auth0_main.tf`:
- Line 12: Disable the Auth0 provider debug logging in the committed Terraform
config by removing or setting the debug flag to false in the auth0 provider
block. Update the auth0_main.tf provider configuration so debug is not enabled
by default, keeping any temporary troubleshooting setting out of checked-in
code.
- Line 6: The Auth0 provider constraint is still too open-ended, so update the
provider version requirement in the auth0_main configuration to a bounded range
instead of only a minimum. Use the existing provider block that sets the
auth0/auth0 version and narrow it to a tested range such as a compatible patch
range or an explicit upper bound below the next major release.

In `@terraform/kubernetes/auth0/generated.tf`:
- Around line 1702-1715: The non_interactive Auth0 client is exposing
browser-based grant types that should not be enabled for this M2M app. Update
the grant_types configuration in the generated Auth0 client definition to keep
only client_credentials, and remove implicit, authorization_code, and
refresh_token unless there is a clear need for browser flows. Use the
non_interactive client block in the generated Terraform output to locate the
change.

In `@terraform/kubernetes/auth0/variables.tf`:
- Line 1: The cloud_domain variable is currently untyped and unvalidated, so
misconfiguration can slip through until runtime. Update the cloud_domain
variable in variables.tf to declare it as a string and add validation that
rejects empty values (and any other invalid format checks you deem appropriate
for callback URL usage). Keep the validation close to the variable definition so
the failure happens early when Terraform plans or applies.

In `@terraform/kubernetes/cloud_deps/base_cloud_deps.tf`:
- Around line 1-4: The kustomization_build data source for the elastic base
dependency is hardcoded to a checkout-specific relative path, which will break
when Terraform runs from a different working directory in CI. Update the elastic
data block to use a parameterized source path from a variable or shared local
used by the cloud_deps Terraform config, and keep the existing data
"kustomization_build" "elastic" identifier so the new path can be reused across
environments.

In `@terraform/kubernetes/cloud_deps/cert_manager.tf`:
- Around line 1-4: The local cert_config is indexing
var.cert_details[var.cloud_domain] too early, so a missing domain triggers an
Invalid index before the custom validation in the cert_manager logic can run.
Update the cert_manager locals/validation flow by using lookup or try when
setting cert_config, or otherwise defer the access into variable validation so
the existing guard for cloud_domain/cert_details can execute first.

In `@terraform/kubernetes/cloud_deps/core_resource_deps.tf`:
- Around line 7-110: The kubernetes_secret_v1 resources are storing sensitive
values in Terraform state via the data fields in cloud_auth, cloud_session,
db_secrets, and cloud_auth0. Refactor the secret generation and secret creation
flow so these values are managed outside Terraform state, either by moving them
to a dedicated secret manager or by provisioning the Kubernetes secrets
out-of-band and only referencing their names in this module; keep the focus on
the data.external sources and the kubernetes_secret_v1 resources involved.

In `@terraform/kubernetes/cloud_deps/env_cloud_deps.tf`:
- Around line 1-4: The kustomization_build data source in env_deps is using a
hardcoded relative path that only works for one local checkout layout. Update
the path resolution in env_cloud_deps.tf so it is derived from the
module/working context instead of a fixed "../../../k8s/cloud_deps/public"
relative path, and make sure the env_deps data block still points to the same
k8s/cloud_deps/public manifests across local runs and Terraform pipelines.

In `@terraform/kubernetes/cloud_deps/provider.tf`:
- Around line 14-22: The kubernetes and kustomization provider blocks are
hardcoded to a developer-specific kubeconfig and context, which prevents
portable auth. Update the provider configuration to use configurable inputs or
environment-based discovery instead of fixed values, and adjust the provider
definitions in provider.tf so they can work in CI and on other machines without
the cockpit-config path or default context being baked in.

---

Outside diff comments:
In `@src/vizier/services/adaptive_export/internal/chhttp/chhttp.go`:
- Around line 107-122: The boot-time ctl.Rehydrate call is using a long-lived
service context, so the new QueryStream path can block startup forever if
ClickHouse stalls. Update the startup flow around ctl.Rehydrate to create and
pass a deadline-bound context instead of the background-derived cancel-only
context, and ensure the timeout is scoped just to rehydration. Use the existing
ctl.Rehydrate call site and the QueryStream behavior in chhttp.Client to locate
the fix.

In `@src/vizier/services/adaptive_export/internal/trigger/clickhouse.go`:
- Around line 322-338: The paging escape in the ClickHouse trigger advances the
watermark past a boundary timestamp without a total row order, which can
permanently skip unseen rows. Update the polling logic in the trigger code path
around the boundary handling to avoid incrementing the watermark by 1ns when the
query is only ordered by normalized event_time. Instead, add a stable secondary
tie-breaker to the pagination/state in the query and cursor logic used by the
trigger so rows at the same timestamp can be paged deterministically without
dropping data.

---

Duplicate comments:
In `@src/vizier/services/adaptive_export/internal/control/server_test.go`:
- Around line 181-189: The query tests in server_test.go cover inverted and
zero-length windows, but they do not exercise the new non-positive timestamp
validation in handleQuery. Add a test case in the existing /query checks using
do(...) with a window where either Window[0] or Window[1] is non-positive, and
assert it returns http.StatusBadRequest; place it alongside the current window
validation cases so the handleQuery contract is pinned by the tests.

In `@src/vizier/services/adaptive_export/internal/controller/controller.go`:
- Around line 344-382: The attribution update in controller logic is mutating
c.active before Sink.Write succeeds, which can let pushPixieRows observe and fan
out an unpersisted snapshot. Fix the flow in the controller method that handles
the row update by staging the modified AttributionRow in a local copy, calling
c.sink.Write first, and only committing the new state into c.active under c.mu
after the write succeeds; keep the rollback path for failures, including the
inFlight bookkeeping, unchanged.

In `@src/vizier/services/adaptive_export/internal/trigger/oracle_test.go`:
- Line 351: The test setup in `Subscribe` is still ignoring the returned error,
which can hide a subscription failure until a later timeout. Update the
`tr.Subscribe(ctx)` call in `oracle_test` to handle the error immediately and
fail the test at the source, using the existing `tr` and `Subscribe` flow rather
than continuing with a potentially nil channel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ffd11bf6-28d9-4236-ba9a-43653a5d3192

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd9c3f and d94624a.

⛔ Files ignored due to path filters (1)
  • terraform/kubernetes/cloud_deps/terraform.tfstate is excluded by !**/*.tfstate
📒 Files selected for processing (49)
  • .github/workflows/cli_release.yaml
  • .github/workflows/cloud_release.yaml
  • .github/workflows/mirror_demos.yaml
  • .github/workflows/mirror_deps.yaml
  • .github/workflows/mirror_releases.yaml
  • .github/workflows/operator_release.yaml
  • .github/workflows/perf_common.yaml
  • .sops.yaml
  • bazel/ui.bzl
  • cockpit/artifact_config.yaml
  • cockpit/auth_deployment.yaml
  • cockpit/cloud_ingress.yaml
  • cockpit/domain_config.yaml
  • cockpit/kustomization.yaml
  • cockpit/oauth_config.yaml
  • cockpit/plugin_db_updater_job.yaml
  • cockpit/proxy_envoy.yaml
  • cockpit/script_bundles_config.yaml
  • cockpit/servers_transport.yaml
  • skaffold/skaffold_cloud.yaml
  • src/vizier/services/adaptive_export/cmd/main.go
  • src/vizier/services/adaptive_export/internal/chhttp/chhttp.go
  • src/vizier/services/adaptive_export/internal/control/server.go
  • src/vizier/services/adaptive_export/internal/control/server_test.go
  • src/vizier/services/adaptive_export/internal/controller/controller.go
  • src/vizier/services/adaptive_export/internal/controller/controller_test.go
  • src/vizier/services/adaptive_export/internal/passthrough/passthrough.go
  • src/vizier/services/adaptive_export/internal/pixieapi/pixieapi.go
  • src/vizier/services/adaptive_export/internal/sink/clickhouse.go
  • src/vizier/services/adaptive_export/internal/sink/fastencode.go
  • src/vizier/services/adaptive_export/internal/sink/integration_test.go
  • src/vizier/services/adaptive_export/internal/streaming/writer.go
  • src/vizier/services/adaptive_export/internal/trigger/clickhouse.go
  • src/vizier/services/adaptive_export/internal/trigger/oracle_test.go
  • terraform/credentials/cockpit/auth0_config.yaml
  • terraform/kubernetes/auth0/.terraform.lock.hcl
  • terraform/kubernetes/auth0/auth0_import.tf
  • terraform/kubernetes/auth0/auth0_main.tf
  • terraform/kubernetes/auth0/generated.tf
  • terraform/kubernetes/auth0/terraform.tfvars
  • terraform/kubernetes/auth0/variables.tf
  • terraform/kubernetes/cloud_deps/.terraform.lock.hcl
  • terraform/kubernetes/cloud_deps/base_cloud_deps.tf
  • terraform/kubernetes/cloud_deps/cert_manager.tf
  • terraform/kubernetes/cloud_deps/core_resource_deps.tf
  • terraform/kubernetes/cloud_deps/env_cloud_deps.tf
  • terraform/kubernetes/cloud_deps/provider.tf
  • terraform/kubernetes/cloud_deps/variables.tf
  • terraform/kubernetes/scripts/create_random_bytes.sh

Comment thread .github/workflows/mirror_demos.yaml
Comment thread bazel/ui.bzl Outdated
Comment on lines +7 to +16
annotations:
external-dns.alpha.kubernetes.io/hostname: >-
test.austrianopencloudcommunity.org,work.test.austrianopencloudcommunity.org
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- test.austrianopencloudcommunity.org
- work.test.austrianopencloudcommunity.org
secretName: cloud-proxy-tls-certs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### transport definition"
sed -n '1,120p' cockpit/servers_transport.yaml

echo "\n### ingress annotations and backends"
sed -n '1,160p' cockpit/cloud_ingress.yaml

echo "\n### repo-wide transport references"
rg -n 'cloud-backend-transport|serverstransport|serversscheme|ServersTransport' cockpit

Repository: k8sstormcenter/pixie

Length of output: 2593


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### broad repo references to cloud-backend-transport"
rg -n 'cloud-backend-transport|ServersTransport|serverstransport|serversTransport|transport' .

echo "\n### candidate service definition"
sed -n '1,220p' cockpit/cloud_proxy_service.yaml 2>/dev/null || true

echo "\n### kustomization context"
sed -n '1,120p' cockpit/kustomization.yaml

Repository: k8sstormcenter/pixie

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "### broad repo references to cloud-backend-transport"
rg -n 'cloud-backend-transport|ServersTransport|serverstransport|serversTransport|transport' .

echo "\n### candidate service definition"
sed -n '1,220p' cockpit/cloud_proxy_service.yaml 2>/dev/null || true

echo "\n### kustomization context"
sed -n '1,120p' cockpit/kustomization.yaml

Repository: k8sstormcenter/pixie

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' cockpit/cloud_proxy_service.yaml 2>/dev/null || true
printf '\n### traefik annotations in cockpit manifests\n'
rg -n 'traefik\.ingress\.kubernetes\.io|serverstransport|ServersTransport' cockpit/*.yaml cockpit/*.yml

Repository: k8sstormcenter/pixie

Length of output: 371


Attach cloud-backend-transport to the / backend

cockpit/cloud_ingress.yaml defines the transport, but the Ingress never references it. Add the Traefik service.serverstransport annotation to the cloud-proxy-service:443 route, otherwise the serverName override is ignored.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cockpit/cloud_ingress.yaml` around lines 7 - 16, The ingress route for
cloud-proxy-service:443 defines the backend transport but never applies it, so
the serverName override is ignored. Update cockpit/cloud_ingress.yaml to attach
the Traefik service.serverstransport annotation on the / backend route and point
it to cloud-backend-transport, using the existing ingress resource so the
backend actually picks up the transport settings.

Comment on lines +15 to +90
spec:
initContainers:
- name: postgres-wait
# TODO(ddelnano): This image was rebuilt from https://github.com/docker-library/postgres 14-alpine
# to remediate a critical vulnerability. Switch back to upstream once fixed.
# yamllint disable-line rule:line-length
image: ghcr.io/pixie-io/postgres:14-alpine-pl1@sha256:237c5fcf79b230979e12fe02f46e0ad29565b4ecb7cb15047197cbb9a6549e8d
command: ['sh', '-c',
'until pg_isready -h ${PL_POSTGRES_HOSTNAME} -p ${PL_POSTGRES_PORT}; do
echo "waiting for postgres";
sleep 2;
done;']
envFrom:
- configMapRef:
name: pl-db-config
containers:
- name: updater
image: cloud-plugin-load_db:latest
envFrom:
- configMapRef:
name: pl-db-config
- configMapRef:
name: pl-domain-config
- configMapRef:
name: pl-tls-config
volumeMounts:
- name: certs
mountPath: /certs
env:
- name: PL_POSTGRES_USERNAME
valueFrom:
secretKeyRef:
name: pl-db-secrets
key: PL_POSTGRES_USERNAME
- name: PL_POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: pl-db-secrets
key: PL_POSTGRES_PASSWORD
- name: PL_JWT_SIGNING_KEY
valueFrom:
secretKeyRef:
name: cloud-auth-secrets
key: jwt-signing-key
- name: PL_PLUGIN_SERVICE
valueFrom:
configMapKeyRef:
name: pl-service-config
key: PL_PLUGIN_SERVICE
- name: PL_PLUGIN_REPO
value: "k8sstormcenter/pixie-plugin"
restartPolicy: Never
volumes:
- name: pl-db-secrets
secret:
secretName: pl-db-secrets
- name: certs
projected:
sources:
- secret:
name: service-tls-server-certs
items:
- key: tls.crt
path: server.crt
- key: tls.key
path: server.key
- key: ca.crt
path: ca.crt
- secret:
name: service-tls-client-certs
items:
- key: tls.crt
path: client.crt
- key: tls.key
path: client.key
backoffLimit: 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Harden this Job before mounting DB and JWT credentials into it.

Both containers inherit the runtime defaults here, so root execution, privilege escalation, and service-account token mounting stay enabled unless the cluster overrides them. That is a poor default for a pod that consumes PL_POSTGRES_PASSWORD and PL_JWT_SIGNING_KEY.

Suggested hardening
 spec:
   ttlSecondsAfterFinished: 10
   template:
     metadata:
       name: plugin-db-updater-job
       labels:
         jobgroup: plugin-db-updater
     spec:
+      automountServiceAccountToken: false
       initContainers:
       - name: postgres-wait
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop: ["ALL"]
+          runAsNonRoot: true
+          seccompProfile:
+            type: RuntimeDefault
         image: ghcr.io/pixie-io/postgres:14-alpine-pl1@sha256:237c5fcf79b230979e12fe02f46e0ad29565b4ecb7cb15047197cbb9a6549e8d
         command: ['sh', '-c',
                   'until pg_isready -h ${PL_POSTGRES_HOSTNAME} -p ${PL_POSTGRES_PORT}; do
                   echo "waiting for postgres";
                   sleep 2;
                   done;']
         envFrom:
         - configMapRef:
             name: pl-db-config
       containers:
       - name: updater
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop: ["ALL"]
+          runAsNonRoot: true
+          seccompProfile:
+            type: RuntimeDefault
         image: cloud-plugin-load_db:latest
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
spec:
initContainers:
- name: postgres-wait
# TODO(ddelnano): This image was rebuilt from https://github.com/docker-library/postgres 14-alpine
# to remediate a critical vulnerability. Switch back to upstream once fixed.
# yamllint disable-line rule:line-length
image: ghcr.io/pixie-io/postgres:14-alpine-pl1@sha256:237c5fcf79b230979e12fe02f46e0ad29565b4ecb7cb15047197cbb9a6549e8d
command: ['sh', '-c',
'until pg_isready -h ${PL_POSTGRES_HOSTNAME} -p ${PL_POSTGRES_PORT}; do
echo "waiting for postgres";
sleep 2;
done;']
envFrom:
- configMapRef:
name: pl-db-config
containers:
- name: updater
image: cloud-plugin-load_db:latest
envFrom:
- configMapRef:
name: pl-db-config
- configMapRef:
name: pl-domain-config
- configMapRef:
name: pl-tls-config
volumeMounts:
- name: certs
mountPath: /certs
env:
- name: PL_POSTGRES_USERNAME
valueFrom:
secretKeyRef:
name: pl-db-secrets
key: PL_POSTGRES_USERNAME
- name: PL_POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: pl-db-secrets
key: PL_POSTGRES_PASSWORD
- name: PL_JWT_SIGNING_KEY
valueFrom:
secretKeyRef:
name: cloud-auth-secrets
key: jwt-signing-key
- name: PL_PLUGIN_SERVICE
valueFrom:
configMapKeyRef:
name: pl-service-config
key: PL_PLUGIN_SERVICE
- name: PL_PLUGIN_REPO
value: "k8sstormcenter/pixie-plugin"
restartPolicy: Never
volumes:
- name: pl-db-secrets
secret:
secretName: pl-db-secrets
- name: certs
projected:
sources:
- secret:
name: service-tls-server-certs
items:
- key: tls.crt
path: server.crt
- key: tls.key
path: server.key
- key: ca.crt
path: ca.crt
- secret:
name: service-tls-client-certs
items:
- key: tls.crt
path: client.crt
- key: tls.key
path: client.key
backoffLimit: 1
spec:
automountServiceAccountToken: false
initContainers:
- name: postgres-wait
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
# TODO(ddelnano): This image was rebuilt from https://github.com/docker-library/postgres 14-alpine
# to remediate a critical vulnerability. Switch back to upstream once fixed.
# yamllint disable-line rule:line-length
image: ghcr.io/pixie-io/postgres:14-alpine-pl1@sha256:237c5fcf79b230979e12fe02f46e0ad29565b4ecb7cb15047197cbb9a6549e8d
command: ['sh', '-c',
'until pg_isready -h ${PL_POSTGRES_HOSTNAME} -p ${PL_POSTGRES_PORT}; do
echo "waiting for postgres";
sleep 2;
done;']
envFrom:
- configMapRef:
name: pl-db-config
containers:
- name: updater
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
image: cloud-plugin-load_db:latest
envFrom:
- configMapRef:
name: pl-db-config
- configMapRef:
name: pl-domain-config
- configMapRef:
name: pl-tls-config
volumeMounts:
- name: certs
mountPath: /certs
env:
- name: PL_POSTGRES_USERNAME
valueFrom:
secretKeyRef:
name: pl-db-secrets
key: PL_POSTGRES_USERNAME
- name: PL_POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: pl-db-secrets
key: PL_POSTGRES_PASSWORD
- name: PL_JWT_SIGNING_KEY
valueFrom:
secretKeyRef:
name: cloud-auth-secrets
key: jwt-signing-key
- name: PL_PLUGIN_SERVICE
valueFrom:
configMapKeyRef:
name: pl-service-config
key: PL_PLUGIN_SERVICE
- name: PL_PLUGIN_REPO
value: "k8sstormcenter/pixie-plugin"
restartPolicy: Never
volumes:
- name: pl-db-secrets
secret:
secretName: pl-db-secrets
- name: certs
projected:
sources:
- secret:
name: service-tls-server-certs
items:
- key: tls.crt
path: server.crt
- key: tls.key
path: server.key
- key: ca.crt
path: ca.crt
- secret:
name: service-tls-client-certs
items:
- key: tls.crt
path: client.crt
- key: tls.key
path: client.key
backoffLimit: 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cockpit/plugin_db_updater_job.yaml` around lines 15 - 90, Harden the Job spec
for the updater workload by setting restrictive pod/security defaults before it
consumes PL_POSTGRES_PASSWORD and PL_JWT_SIGNING_KEY. Update the Job template in
plugin_db_updater_job.yaml to disable root execution and privilege escalation,
drop unnecessary Linux capabilities, and prevent automatic service-account token
mounting; apply these settings at the pod-level and, if needed, reinforce them
on the postgres-wait and updater containers.

Source: Linters/SAST tools

Comment thread cockpit/proxy_envoy.yaml
Comment on lines +43 to +50
cors:
allow_origin_string_match:
- suffix: "test.austrianopencloudcommunity.org"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: content-type,x-grpc-web,x-user-agent
max_age: "1728000"
expose_headers: grpc-status,grpc-message,grpc-timeout
allow_credentials: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use an exact CORS origin match here.

suffix: "test.austrianopencloudcommunity.org" is wider than the single host configured in cockpit/domain_config.yaml and will also match origins like https://eviltest.austrianopencloudcommunity.org. With allow_credentials: true, that broadens who can make credentialed browser requests.

Suggested fix
                   cors:
                     allow_origin_string_match:
-                    - suffix: "test.austrianopencloudcommunity.org"
+                    - exact: "https://test.austrianopencloudcommunity.org"
                     allow_methods: GET, PUT, DELETE, POST, OPTIONS
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cors:
allow_origin_string_match:
- suffix: "test.austrianopencloudcommunity.org"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: content-type,x-grpc-web,x-user-agent
max_age: "1728000"
expose_headers: grpc-status,grpc-message,grpc-timeout
allow_credentials: true
cors:
allow_origin_string_match:
- exact: "https://test.austrianopencloudcommunity.org"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: content-type,x-grpc-web,x-user-agent
max_age: "1728000"
expose_headers: grpc-status,grpc-message,grpc-timeout
allow_credentials: true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cockpit/proxy_envoy.yaml` around lines 43 - 50, The CORS configuration in the
Envoy proxy is using a suffix-based origin match, which is broader than the
single approved host and can match unintended subdomains. Update the cors
allow_origin_string_match setting to use an exact origin match for the same host
configured in cockpit/domain_config.yaml, and keep the rest of the cors settings
unchanged so credentialed requests are only allowed from that one origin.

Comment on lines +1 to +4
data "kustomization_build" "elastic" {
# TODO(ddelnano): This will need to be updated for the terraform Azure pipeline
path = "../../../k8s/cloud_deps/base/elastic/operator"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Parameterize the kustomize source path before this is used in CI.

This only works from the current checkout layout. The TODO already calls out the Azure pipeline case; if Terraform runs from a different working directory, kustomization_build will fail before any base deps are applied.

Suggested fix
 data "kustomization_build" "elastic" {
-  # TODO(ddelnano): This will need to be updated for the terraform Azure pipeline
-  path = "../../../k8s/cloud_deps/base/elastic/operator"
+  path = abspath("${path.module}/../../../k8s/cloud_deps/base/elastic/operator")
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data "kustomization_build" "elastic" {
# TODO(ddelnano): This will need to be updated for the terraform Azure pipeline
path = "../../../k8s/cloud_deps/base/elastic/operator"
}
data "kustomization_build" "elastic" {
path = abspath("${path.module}/../../../k8s/cloud_deps/base/elastic/operator")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/kubernetes/cloud_deps/base_cloud_deps.tf` around lines 1 - 4, The
kustomization_build data source for the elastic base dependency is hardcoded to
a checkout-specific relative path, which will break when Terraform runs from a
different working directory in CI. Update the elastic data block to use a
parameterized source path from a variable or shared local used by the cloud_deps
Terraform config, and keep the existing data "kustomization_build" "elastic"
identifier so the new path can be reused across environments.

Comment on lines +1 to +4
locals {
cert_subdomain = var.cloud_domain
cert_config = var.cert_details[var.cloud_domain]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The cert_details guard never gets a chance to run.

Line 3 indexes var.cert_details[var.cloud_domain] while evaluating locals, so a missing domain fails with an Invalid index before Line 49's custom error can fire. Use lookup/try in the local, or move this to variable validation.

Suggested fix
 locals {
   cert_subdomain = var.cloud_domain
-  cert_config    = var.cert_details[var.cloud_domain]
+  cert_config    = lookup(var.cert_details, var.cloud_domain, null)
 }
@@
   lifecycle {
     precondition {
-      condition     = contains(keys(var.cert_details), var.cloud_domain)
+      condition     = local.cert_config != null
       error_message = "cloud_domain '${var.cloud_domain}' must have a corresponding entry in cert_details."
     }
   }
 }

Also applies to: 48-53

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/kubernetes/cloud_deps/cert_manager.tf` around lines 1 - 4, The
local cert_config is indexing var.cert_details[var.cloud_domain] too early, so a
missing domain triggers an Invalid index before the custom validation in the
cert_manager logic can run. Update the cert_manager locals/validation flow by
using lookup or try when setting cert_config, or otherwise defer the access into
variable validation so the existing guard for cloud_domain/cert_details can
execute first.

Comment thread terraform/kubernetes/cloud_deps/core_resource_deps.tf
Comment thread terraform/kubernetes/cloud_deps/env_cloud_deps.tf
Comment thread terraform/kubernetes/cloud_deps/provider.tf
pixie-agent added 2 commits June 28, 2026 08:54
cb81ecd re-added the terraform/ tree (16 files, 9817 lines) while
fixing regressions from the origin/main pickup, because the main tip
53d09cc had deleted it. That restoration is unrelated to the
adaptive_export auth work in this PR; it is now carried on its own
branch (restore-terraform-main, off origin/main) for a separate PR
into main. Removing it here keeps PR 68 scoped to the AE feature.
These were re-added on this branch while fixing the origin/main pickup
regression (cb81ecd + the d94624a cockpit rename), but they are fork
infra deleted from main by 53d09cc, not part of the adaptive_export
auth work. Carried on restore-fork-infra-main for a separate PR into
main. skaffold/skaffold_cloud.yaml is left in place — it is an upstream
file, not a clean fork-only add.
// Cert/key from the service-tls-certs secret the broker/PEM already use
// (mounted /certs); dx skip-verifies. Default-OFF for incremental rollout.
var err error
if os.Getenv("CONTROL_TLS") == "true" {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude, can we assume that CONTROL_TLS is true and only implement a fallback if it is not. But it should NOT boot if TLS is off

# Conflicts:
#	skaffold/skaffold_cloud.yaml
#	src/e2e_test/adaptive_export_loadtest/README.md
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR expands adaptive export’s ClickHouse schema, control-surface security, trigger polling, persistence handling, streaming behavior, and live Go load-test suite. It also mounts TLS certificates, adds a direct TLS client option, updates runner labels, and excludes an unconverted load generator from Bazel.

Changes

Adaptive export runtime and validation

Layer / File(s) Summary
ClickHouse schema and trigger polling
src/vizier/services/adaptive_export/internal/clickhouse/schema.sql, src/vizier/services/adaptive_export/internal/trigger/*
Adds forensic storage objects, normalized event-time query coverage, boundary paging, and oracle tests for polling behavior.
Control surface and deployment security
src/api/go/pxapi/opts.go, src/vizier/services/adaptive_export/cmd/main.go, src/vizier/services/adaptive_export/internal/control/*, k8s/vizier/bootstrap/adaptive_export_deployment.yaml
Adds direct TLS skip verification, optional JWT/TLS control serving, request limits and validation, control tests, resource limits, and certificate mounts.
Runtime persistence and streaming reliability
src/vizier/services/adaptive_export/internal/controller/*, internal/passthrough/*, internal/chhttp/*, internal/streaming/*, internal/sink/*, internal/pixieapi/*
Adds attribution rollback and rehydration fan-out, bounded pulls, timeout-free streaming clients, retained failed batches, non-finite number rejection, and logging/test updates.
Live adaptive-export load-test suite
src/e2e_test/adaptive_export_loadtest/*
Replaces the shell-oriented documentation with a Go module containing live environment helpers, ClickHouse injection and queries, deterministic fixtures, KPI assertions, and staged tests.
CI and repository infrastructure
.bazelignore, .github/workflows/*
Excludes the Docker-built load generator from Bazel and changes release, mirror, and performance jobs to Oracle VM runners.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and reflects the main change: AE control auth with broader follow-up work.
Description check ✅ Passed The description matches the changeset and summarizes AE control auth, trigger, controller, and related follow-up fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ae-followup-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Entlein added 4 commits July 16, 2026 12:48
…fixtures + KPIs)

Collapse the 12-script shell harness (+ stats.py) into a table-driven Go test
suite under suite/: fixtures (control-plane experiments), KPI asserts via
testify/require (reproducibility/reconcile/reduction), and one runner driving a
deployed AE image on a rig (live-gated by AELOAD_LIVE). Removes all CVE names and
adversarial verbs from the harness; kubescape/k8s wire tokens stay literal.
Volume reduction is a measured outcome to report, not an invariant — asserting
'reduction >= minPct' gates on a made-up threshold. Removed RequireReductionAtLeast;
the reduction test now measures + logs the firehose->steered delta without a pass/fail gate.
…test)

kubescape_logs.event_time is unix NANOSECONDS (Vector kubescape_enrich emits ns).
The DDL read it as seconds via toDateTime() -> ns overflow -> wrong partitions +
rows born already-TTL-expired (the F1/F8 unit bug). Fix, matching soc clickhouse-lab:
  PARTITION BY toYYYYMM(fromUnixTimestamp64Nano(event_time))
  TTL         toDateTime(fromUnixTimestamp64Nano(event_time)) + INTERVAL 30 DAY
Also raise the protocol tables' derived event_time DateTime64(3) (ms) -> DateTime64(9)
(ns), matching time_. The loadtest suite now injects UnixNano and documents C1 as ns.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant