|
| 1 | +# Plan: Progressive Improvement of Tulip |
| 2 | + |
| 3 | +**TL;DR:** Address the highest-impact issues first (performance regressions), then improve code robustness (error handling, context propagation), then clean up UX hacks. Each phase is independently shippable. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Phase 1 — Performance Quick Wins (~2–3 days, highest ROI) |
| 8 | + |
| 9 | +**1.1 Fix N+1 signature queries** — services/cmd/api/api.go ~L236–256 |
| 10 | +Currently fetches each SuricataSig individually in a loop per flow. Add `GetSignaturesBatch(ctx, ids []string) []SuricataSig` to services/pkg/db/db.go backed by a `$in` query in services/pkg/db/mongo.go. 50 flows × 3 sigs = 150 round trips → 1. |
| 11 | + |
| 12 | +**1.2 Add compound index for enricher lookups** — services/pkg/db/mongo.go ~L201–206 |
| 13 | +`AddSignatureToFlow` and `AddTagsToFlow` filter on `{src_ip, dst_ip, src_port, dst_port, time}` but no compound index covers this. Add index: `{src_ip: 1, dst_ip: 1, src_port: 1, dst_port: 1, time: 1}`. |
| 14 | + |
| 15 | +**1.3 Fix redundant tag aggregation** — services/pkg/db/mongo.go ~L60–111 |
| 16 | +`GetTagList()` runs two full collection scans and merges with an O(n²) loop. Replace with a single authoritative source: keep only the `tags` collection lookup (fed by `InsertTag` calls which already exist) and drop the flows aggregation pass. |
| 17 | + |
| 18 | +**1.4 Add text index for flow content search** — services/pkg/db/mongo.go ~L465 |
| 19 | +`flow.data` regex search does a full collection scan. Add a denormalized `search_data` root-level field populated at insert time by joining all `FlowItem.Data` values — enabling the existing text index to cover it. |
| 20 | + |
| 21 | +--- |
| 22 | + |
| 23 | +## Phase 2 — Robustness & API Correctness (~3–4 days) |
| 24 | + |
| 25 | +**2.1 Propagate request context through DB calls** — services/pkg/db/mongo.go |
| 26 | +31 uses of `context.TODO()` mean in-flight DB queries can't be cancelled on client disconnect or timeout. Change all DB interface methods in services/pkg/db/db.go to accept `ctx context.Context` and pass the Echo request context from services/cmd/api/api.go. |
| 27 | + |
| 28 | +**2.2 Return errors from tag inserts** — services/pkg/db/mongo.go ~L415–424 |
| 29 | +`InsertTags` / `InsertTag` silently swallow all errors. Change to return `error`; callers can ignore duplicate-key errors specifically (`mongo.IsDuplicateKeyError`) and log others. |
| 30 | + |
| 31 | +**2.3 Fix empty-tags edge case** — services/cmd/api/api.go + frontend/src/api.ts ~L33 |
| 32 | +Empty `includeTags`/`excludeTags` arrays cause incorrect query behavior (known TODO with a beer bounty). Backend: guard — if array is empty, skip the `$all`/`$nin` clause. Remove the frontend workaround once backend handles it. |
| 33 | + |
| 34 | +**2.4 Add pagination metadata to `/query` response** — services/cmd/api/api.go ~L166 |
| 35 | +Wrap the response: `{data: [...], meta: {total: N, hasMore: bool}}`. Run `CountFlows` in parallel with `GetFlows`. Frontend can use `hasMore` to disable infinite scroll rather than guessing. |
| 36 | + |
| 37 | +--- |
| 38 | + |
| 39 | +## Phase 3 — Technical Debt & UX Hacks (~1 week, incremental) |
| 40 | + |
| 41 | +**3.1 Fix timezone display in chart** — frontend/src/components/Corrie.tsx ~L235, L324 |
| 42 | +FIXME comment: datetime axis shows wrong timezone. Audit Chart.js time scale config; use UTC-aware formatting with `date-fns-tz` or standardize on UTC display throughout. |
| 43 | + |
| 44 | +**3.2 Replace O(n) flow index scan with Map** — frontend/src/pages/FlowList.tsx ~L251–280 |
| 45 | +Keyboard navigation triggers a linear search over all loaded flows on each keypress. Replace with a `Map<flowId, index>` maintained alongside the flow list — O(1) lookup. |
| 46 | + |
| 47 | +**3.3 Fix scroll ↔ keyboard focus desync** — frontend/src/pages/FlowView.tsx ~L579 |
| 48 | +TODO: manual scroll doesn't update `currentFlow`, causing keyboard navigation to jump. Add an `IntersectionObserver` or scroll event listener to sync the active flow with the visible item. |
| 49 | + |
| 50 | +**3.4 Decouple API layer from BSON** — services/cmd/api/api.go ~L73 |
| 51 | +TODO comment: "API layer should not be aware of database structure." Move filter-building logic into a `db.FlowQuery` struct in services/pkg/db/db.go; api.go populates the struct, mongo.go translates it to BSON. Enables unit-testing API logic without a real DB. |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +## Verification per Phase |
| 56 | +- **Phase 1:** Benchmark `/query` + enrichment time before/after with `mongosh` + `hey`; run existing Go tests |
| 57 | +- **Phase 2:** Integration test empty-tag query; confirm context cancellation propagates; check `/query` response shape in frontend |
| 58 | +- **Phase 3:** Manual test keyboard nav + scroll; regression test chart rendering; run `npx eslint` on changed frontend files |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## Priority order if time is limited |
| 63 | +1. Step 1.1 (N+1 fix) — biggest single perf gain, small code change |
| 64 | +2. Step 1.2 (compound index) — zero-risk, one line |
| 65 | +3. Step 2.1 (context propagation) — correctness, enables cancellation |
| 66 | +4. Step 2.3 (empty tags) — fixes a known bug with a beer bounty on it |
| 67 | + |
| 68 | +## Decisions |
| 69 | +- MongoDB stays — migration not worth it at CTF scale |
| 70 | +- Each phase is independently mergeable |
| 71 | +- Step 3.4 (BSON decoupling) is optional/long-term — only pursue if a DB swap becomes relevant later |
0 commit comments