forked from pixie-io/pixie
-
Notifications
You must be signed in to change notification settings - Fork 2
adaptive_export: /query runner + dx_evidence_graph rename + evidence_manifest (folds #77/#78) #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
3dd4385
feat(adaptive_export): wire the /query runner — dx OrderQuery → foren…
42bd615
refactor(ae): rename dx_attack_graph → dx_evidence_graph (malignant v…
da37ced
feat(ae): add /dx/evidence_manifest ingest → forensic_db.dx_evidence_…
0326bbb
lint(#73): fix arc-lint failures + copy.bara cleanup
086a9cc
genfiles(#73): .bazelignore the aeloadsuite nested module
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,18 +54,25 @@ type queryRunner interface { | |
| } | ||
|
|
||
| // graphWriter persists dx evidence-graph edges (newline-delimited JSON, | ||
| // JSONEachRow) to forensic_db.dx_attack_graph. nil → /dx/attack_graph 501s. | ||
| // JSONEachRow) to forensic_db.dx_evidence_graph. nil → /dx/evidence_graph 501s. | ||
| type graphWriter interface { | ||
| WriteAttackGraph(ctx context.Context, jsonEachRow []byte) error | ||
| WriteEvidenceGraph(ctx context.Context, jsonEachRow []byte) error | ||
| } | ||
|
|
||
| // manifestWriter persists one dx §9 completeness manifest per verdict | ||
| // (JSONEachRow) to forensic_db.dx_evidence_manifest. nil → /dx/evidence_manifest 501s. | ||
| type manifestWriter interface { | ||
| WriteEvidenceManifest(ctx context.Context, jsonEachRow []byte) error | ||
| } | ||
|
|
||
| // Server is the control HTTP surface. | ||
| type Server struct { | ||
| set exporter | ||
| runner queryRunner // may be nil; /query then returns 501 | ||
| graph graphWriter // may be nil; /dx/attack_graph then returns 501 | ||
| mux *http.ServeMux | ||
| verify func(bearer string) error // nil → auth disabled; set via SetAuth | ||
| set exporter | ||
| runner queryRunner // may be nil; /query then returns 501 | ||
| graph graphWriter // may be nil; /dx/evidence_graph then returns 501 | ||
| manifest manifestWriter // may be nil; /dx/evidence_manifest then returns 501 | ||
| mux *http.ServeMux | ||
| verify func(bearer string) error // nil → auth disabled; set via SetAuth | ||
| } | ||
|
|
||
| // New builds the control server. runner may be nil for deployments that | ||
|
|
@@ -76,13 +83,17 @@ func New(set exporter, runner queryRunner) *Server { | |
| s.mux.HandleFunc("/export/start", s.handleStart) | ||
| s.mux.HandleFunc("/export/stop", s.handleStop) | ||
| s.mux.HandleFunc("/query", s.handleQuery) | ||
| s.mux.HandleFunc("/dx/attack_graph", s.handleDXAttackGraph) | ||
| s.mux.HandleFunc("/dx/evidence_graph", s.handleDXEvidenceGraph) | ||
| s.mux.HandleFunc("/dx/evidence_manifest", s.handleDXEvidenceManifest) | ||
| return s | ||
| } | ||
|
|
||
| // SetGraphWriter wires the dx_attack_graph sink. | ||
| // SetGraphWriter wires the dx_evidence_graph sink. | ||
| func (s *Server) SetGraphWriter(g graphWriter) { s.graph = g } | ||
|
|
||
| // SetManifestWriter wires the dx_evidence_manifest sink. | ||
| func (s *Server) SetManifestWriter(m manifestWriter) { s.manifest = m } | ||
|
|
||
| // SetAuth turns on bearer-JWT auth for the control surface, verified with the | ||
| // SAME shared lib + signing key the vizier broker/PEM use (px.dev/pixie/src/ | ||
| // shared/services/utils). dx already mints a service JWT (GenerateJWTForService, | ||
|
|
@@ -115,9 +126,9 @@ func (s *Server) Handler() http.Handler { | |
| }) | ||
| } | ||
|
|
||
| // handleDXAttackGraph ingests a JSON array of dx evidence-graph edges and writes | ||
| // them to forensic_db.dx_attack_graph (as JSONEachRow). | ||
| func (s *Server) handleDXAttackGraph(w http.ResponseWriter, r *http.Request) { | ||
| // handleDXEvidenceGraph ingests a JSON array of dx evidence-graph edges and writes | ||
| // them to forensic_db.dx_evidence_graph (as JSONEachRow). | ||
| func (s *Server) handleDXEvidenceGraph(w http.ResponseWriter, r *http.Request) { | ||
| if r.Method != http.MethodPost { | ||
| w.WriteHeader(http.StatusMethodNotAllowed) | ||
| return | ||
|
|
@@ -140,13 +151,88 @@ func (s *Server) handleDXAttackGraph(w http.ResponseWriter, r *http.Request) { | |
| buf.Write(e) | ||
| buf.WriteByte('\n') | ||
| } | ||
| if err := s.graph.WriteAttackGraph(r.Context(), buf.Bytes()); err != nil { | ||
| if err := s.graph.WriteEvidenceGraph(r.Context(), buf.Bytes()); err != nil { | ||
| w.WriteHeader(http.StatusBadGateway) | ||
| return | ||
| } | ||
| w.WriteHeader(http.StatusAccepted) | ||
| } | ||
|
|
||
| // dxManifest mirrors the wire shape of dx's manifest.Manifest (internal/manifest). | ||
| // Scalars map to typed forensic_db.dx_evidence_manifest columns; the nested | ||
| // collections are held as raw JSON and persisted as JSON text in String columns | ||
| // so the JSONEachRow insert is ClickHouse-version independent. | ||
| type dxManifest struct { | ||
| InvestigationID string `json:"investigation_id"` | ||
| EventTime int64 `json:"event_time"` | ||
| Hostname string `json:"hostname"` | ||
| Condition string `json:"condition"` | ||
| Verdict string `json:"verdict"` | ||
| Confidence float64 `json:"confidence"` | ||
| Posterior float64 `json:"posterior"` | ||
| CatalogVersion string `json:"catalog_version"` | ||
| CaseWindow json.RawMessage `json:"case_window"` | ||
| Findings json.RawMessage `json:"findings"` | ||
| Orders json.RawMessage `json:"orders"` | ||
| Seeds json.RawMessage `json:"seeds"` | ||
| Chain json.RawMessage `json:"chain"` | ||
| EvidenceHash string `json:"evidence_hash"` | ||
| } | ||
|
|
||
| // handleDXEvidenceManifest ingests ONE dx completeness manifest (per verdict) | ||
| // and writes it to forensic_db.dx_evidence_manifest as a single JSONEachRow | ||
| // row, with the nested collections rendered as JSON text. | ||
| func (s *Server) handleDXEvidenceManifest(w http.ResponseWriter, r *http.Request) { | ||
| if r.Method != http.MethodPost { | ||
| w.WriteHeader(http.StatusMethodNotAllowed) | ||
| return | ||
| } | ||
| if s.manifest == nil { | ||
| w.WriteHeader(http.StatusNotImplemented) | ||
| return | ||
| } | ||
| var m dxManifest | ||
| if !decode(w, r, &m) { | ||
| w.WriteHeader(http.StatusBadRequest) | ||
| return | ||
| } | ||
| row := map[string]any{ | ||
| "investigation_id": m.InvestigationID, | ||
| "event_time": m.EventTime, | ||
| "hostname": m.Hostname, | ||
| "condition": m.Condition, | ||
| "verdict": m.Verdict, | ||
| "confidence": m.Confidence, | ||
| "posterior": m.Posterior, | ||
| "catalog_version": m.CatalogVersion, | ||
| "case_window": jsonText(m.CaseWindow), | ||
| "findings": jsonText(m.Findings), | ||
| "orders": jsonText(m.Orders), | ||
| "seeds": jsonText(m.Seeds), | ||
| "chain": jsonText(m.Chain), | ||
| "evidence_hash": m.EvidenceHash, | ||
| } | ||
| line, err := json.Marshal(row) | ||
| if err != nil { | ||
| w.WriteHeader(http.StatusBadRequest) | ||
| return | ||
| } | ||
| if err := s.manifest.WriteEvidenceManifest(r.Context(), append(line, '\n')); err != nil { | ||
|
Comment on lines
+194
to
+220
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Enforce the evidence contract before returning 202. Both ingestion paths currently permit incomplete or schema-mismatched evidence to be stored with default values.
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| w.WriteHeader(http.StatusBadGateway) | ||
| return | ||
| } | ||
| w.WriteHeader(http.StatusAccepted) | ||
| } | ||
|
|
||
| // jsonText renders a nested JSON value as compact text for a String column; | ||
| // nil/absent/null → "" so the column holds an empty string rather than "null". | ||
| func jsonText(raw json.RawMessage) string { | ||
| if len(raw) == 0 || string(raw) == "null" { | ||
| return "" | ||
| } | ||
| return string(raw) | ||
| } | ||
|
|
||
| // ── wire types ──────────────────────────────────────────────────────── | ||
| type targetReq struct { | ||
| Namespace string `json:"namespace"` | ||
|
|
@@ -175,7 +261,7 @@ func (t targetReq) target() anomaly.Target { | |
| } | ||
|
|
||
| // maxControlBodyBytes caps a single control-surface request body. The | ||
| // largest legitimate payload we accept is /dx/attack_graph which is a | ||
| // largest legitimate payload we accept is /dx/evidence_graph which is a | ||
| // JSON array of pre-marshalled JSONEachRow lines — measured live the | ||
| // hottest dx rule-in pass fits in ~256 KiB. 4 MiB is well above that | ||
| // and below the per-pod memory headroom an oversized POST could | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Do not gate
/queryon anomaly-driven push mode.The controller only receives a
PixieQuerierwhenPushPixieTablesis non-empty, so enablingCONTROL_ADDRalone exposes a/queryendpoint that always returns 502. Create and attach the adapter whenever control-ordered queries are enabled.🤖 Prompt for AI Agents