Skip to content

feat(api): transaction query endpoint with DSL + cursor pagination (Q3 §6.1)#398

Merged
remyluslosius merged 1 commit into
mainfrom
feat/q3-transaction-query-api
Apr 14, 2026
Merged

feat(api): transaction query endpoint with DSL + cursor pagination (Q3 §6.1)#398
remyluslosius merged 1 commit into
mainfrom
feat/q3-transaction-query-api

Conversation

@remyluslosius
Copy link
Copy Markdown
Contributor

First slice of Q3 §6.1 — Transaction Log Query API. Per `docs/OPENWATCH_Q1_Q3_PLAN.md`, this is the foundation for the future Agent API and the <500ms historical posture query KPI.

Summary

Adds `POST /api/transactions/query` — a machine-friendly structured query endpoint for the transaction log. Complements the existing `GET /api/transactions` (kept for UI list views) with features suited to programmatic use:

Feature Detail
DSL in POST body Structured JSON request vs query string (no URL-length limit)
Multi-value filters `host_ids`, `rule_ids`, `status`, `phase`, `severity`, `initiator_type` as lists (IN clauses)
Fleet filter `fleet_id` resolves via EXISTS subquery on `host_group_memberships`
Framework filter JSONB `?` operator on `framework_refs`
Cursor pagination Opaque base64(JSON({started_at, id})) with tuple compare for deterministic tie-break
Projection `fields` allow-list; unknown fields return HTTP 400; default excludes heavy JSONB
Response `items`, `total_count`, `next_cursor` (null on last page)
Audit logging Every query writes a `TRANSACTION_QUERY` audit event with user + filter summary

Out of scope (tracked for follow-up PR)

  • Sort DSL (request-specified ORDER BY beyond the default started_at DESC, id DESC)
  • Per-API-key rate limiting
  • p95 <500ms benchmark enforcement in CI

The scope above are explicitly listed in the spec's `out_of_scope` section.

Spec + tests

  • New spec: `specs/api/transactions/transaction-query.spec.yaml` (draft, 10 ACs)
  • New test: `tests/backend/unit/api/test_transaction_query_spec.py` (source-inspection pattern, one test class per AC)
  • Spec coverage: 823/823 ACs at 100% (`check-spec-coverage.py`)

Security

  • RBAC: GUEST+ (matches existing `GET /api/transactions`)
  • All list filter values use parameterized IN clauses (spec AC-8)
  • Fleet filter uses EXISTS subquery, not inline IN list (avoids ID-list blowup)
  • Cursor is opaque to clients — no ability to inject SQL via cursor body because decoded values land in parameterized placeholders
  • Every query produces an audit log row with user + filter-summary (no filter values, to avoid logging sensitive host IDs)

Test plan

  • Pydantic schema validates the full DSL
  • Source-inspection tests cover all 10 ACs locally
  • `python3 scripts/validate-specs.py` passes (95/95)
  • `python3 scripts/check-spec-coverage.py --enforce-active` passes (823/823)
  • black + isort + flake8 clean
  • CI pipeline passes on this PR
  • Follow-up PR lands sort DSL, rate limit, and perf test

Q3 §6.1 first slice — machine-friendly structured query endpoint for the
transaction log. Complements the existing GET /api/transactions (kept for
UI list views) with a DSL body, cursor pagination, and field projection
suited to Agent API use cases.

Features:
- POST body DSL with single + list filters (host_ids, rule_ids, status,
  phase, severity, initiator_type). Multi-value filters use parameterized
  IN clauses.
- fleet_id filter resolves via EXISTS subquery against
  host_group_memberships (not a huge inline IN list).
- Cursor pagination: opaque base64(json({started_at, id})) with tuple
  comparison in the WHERE clause for deterministic tie-break on equal
  timestamps.
- Projection: fields list restricted to an allow-list; unknown fields
  return HTTP 400. Default excludes heavy JSONB columns.
- Response includes next_cursor (null on last page) and total_count
  (ignores cursor, only filters).

Scope intentionally limited to the foundation. Follow-up PR will add:
- Sort DSL (request-specified ORDER BY beyond default)
- Per-API-key rate limiting
- p95 <500ms benchmark enforcement in CI

Spec: specs/api/transactions/transaction-query.spec.yaml (draft, 10 ACs).
Tests: tests/backend/unit/api/test_transaction_query_spec.py (source-
inspection pattern, all 10 ACs covered). Spec coverage 823/823 at 100%.
if f in ("evidence_envelope", "framework_refs") and isinstance(val, str):
try:
val = json.loads(val)
except (json.JSONDecodeError, ValueError):
assert not missing, f"TransactionQueryRequest missing: {missing}"

def test_route_uses_parameterized_in_clauses(self):
import app.routes.transactions.query as mod
assert ":phase_" in source

def test_fleet_id_uses_subquery(self):
import app.routes.transactions.query as mod
assert "SELECT host_id FROM host_group_memberships" in source

def test_framework_uses_jsonb_operator(self):
import app.routes.transactions.query as mod
assert fields == {"items", "total_count", "next_cursor"}

def test_route_applies_cursor_with_tuple_compare(self):
import app.routes.transactions.query as mod
"""AC-5: Default ORDER BY started_at DESC, id DESC."""

def test_query_uses_stable_ordering(self):
import app.routes.transactions.query as mod
"""AC-6: Requires GUEST+; audit logger writes on each query."""

def test_route_requires_role(self):
import app.routes.transactions.query as mod
assert "UserRole.GUEST" in source

def test_route_writes_audit_log(self):
import app.routes.transactions.query as mod
assert exc_info.value.status_code == 400

def test_date_range_inversion_check_present(self):
import app.routes.transactions.query as mod
"""AC-8: All queries parameterized; no string-concat user input."""

def test_all_filter_values_use_named_params(self):
import app.routes.transactions.query as mod
@remyluslosius remyluslosius merged commit c98cf92 into main Apr 14, 2026
20 of 21 checks passed
@remyluslosius remyluslosius deleted the feat/q3-transaction-query-api branch April 14, 2026 13:41
remyluslosius added a commit that referenced this pull request Apr 14, 2026
Q3 §6.1 endpoint merged in PR #398 has a stable HTTP surface but an
interim implementation. Per the Kensa↔OpenWatch coordination
(docs/KENSA_OPENWATCH_COORDINATION_2026-04-14.md and the Kensa
team's response at kensa/docs/KENSA_OPENWATCH_RESPONSE_2026-04-14.md),
the implementation migrates to delegate into Kensa's Go api/ surface at
Kensa Week 22.

Changes:
- transaction-query.spec.yaml bumped to 1.1 with interim_implementation
  frontmatter naming the specific Kensa api/ methods it converges onto
  (LogQuery.Query, LogQuery.Get, LogQuery.Aggregate)
- Route file docstring updated with a prominent INTERIM IMPLEMENTATION
  header pointing at the Kensa convergence target and the coordination
  docs

No behavioral changes. No endpoint surface changes. This establishes the
convergence-annotation pattern that Kensa counter-asked for in their
response §6.1, so that future reviewers can grep for stale interim
annotations past their convergence week.
remyluslosius added a commit that referenced this pull request Apr 14, 2026
Q3 §6.1 endpoint merged in PR #398 has a stable HTTP surface but an
interim implementation. Per the Kensa↔OpenWatch coordination
(docs/KENSA_OPENWATCH_COORDINATION_2026-04-14.md and the Kensa
team's response at kensa/docs/KENSA_OPENWATCH_RESPONSE_2026-04-14.md),
the implementation migrates to delegate into Kensa's Go api/ surface at
Kensa Week 22.

Changes:
- transaction-query.spec.yaml bumped to 1.1 with interim_implementation
  frontmatter naming the specific Kensa api/ methods it converges onto
  (LogQuery.Query, LogQuery.Get, LogQuery.Aggregate)
- Route file docstring updated with a prominent INTERIM IMPLEMENTATION
  header pointing at the Kensa convergence target and the coordination
  docs

No behavioral changes. No endpoint surface changes. This establishes the
convergence-annotation pattern that Kensa counter-asked for in their
response §6.1, so that future reviewers can grep for stale interim
annotations past their convergence week.
remyluslosius added a commit that referenced this pull request Apr 14, 2026
Third and final PR in the Kensa↔OpenWatch coordination batch (PRs #399,
#400, this one). Updates the Q1-Q3 plan to reflect the architectural
commitments made in the 2026-04-14 coordination.

.gitignore:
- Whitelist docs/OPENWATCH_Q1_PLAN.md, Q2_PLAN.md, Q1_Q3_PLAN.md,
  VISION.md, VISION_STATUS.md, and KENSA_OPENWATCH_COORDINATION_*.md
  so cross-team coordination docs can reference them and the Kensa
  convergence schedule is reviewable.

docs/OPENWATCH_Q1_Q3_PLAN.md (rewrite):
- Add top-level Kensa Convergence Addendum documenting:
    - The "GitHub over Kensa's git" posture
    - Section-by-section table of what changes under the addendum
    - What stays purely OpenWatch-layer (SSO, notifications, 6.3, 6.4, 6.5)
    - Kensa milestone convergence table (Week 1, 22, 24, 25, 26, 40)
    - The interim_implementation: frontmatter convention
- Rewrite §6.1 (Transaction Log Query API): endpoint URL + schema
  stable; implementation swaps to kensa.api.Kensa.TransactionLog() at
  Kensa Week 22. First slice shipped as PR #398; annotated in PR #399.
- Rewrite §6.2 (Proactive Remediation Workflow): original draft had
  OpenWatch generating the plan; revised architecture wraps
  Kensa.Plan / Kensa.Execute with an approval UI. Detailed flow
  diagram, ownership split, blocking dependency on Kensa Week 24.
- Update §3.4 (Fleet health): PostgreSQL transactions table reframed
  as derived cache; Kensa.Subscribe feeds drift counts at Week 25.

docs/KENSA_OPENWATCH_COORDINATION_2026-04-14.md (now tracked):
- The outbound coordination memo becomes a first-class committed
  artifact. Already written 2026-04-14; this commit tracks it so the
  Kensa response (at kensa/docs/...) can reference it without
  cross-repo submodule complexity.

docs/OPENWATCH_Q1_PLAN.md, Q2_PLAN.md, VISION.md, VISION_STATUS.md:
- Newly tracked. Earlier session edits (FreeBSD removal markers) are
  already in these local copies and are committed as-is.

No code changes. This is the plan-doc counterpart to PR #399 (query
interim annotation) and PR #400 (signing scope narrow).
remyluslosius added a commit that referenced this pull request Apr 14, 2026
…#399)

Q3 §6.1 endpoint merged in PR #398 has a stable HTTP surface but an
interim implementation. Per the Kensa↔OpenWatch coordination
(docs/KENSA_OPENWATCH_COORDINATION_2026-04-14.md and the Kensa
team's response at kensa/docs/KENSA_OPENWATCH_RESPONSE_2026-04-14.md),
the implementation migrates to delegate into Kensa's Go api/ surface at
Kensa Week 22.

Changes:
- transaction-query.spec.yaml bumped to 1.1 with interim_implementation
  frontmatter naming the specific Kensa api/ methods it converges onto
  (LogQuery.Query, LogQuery.Get, LogQuery.Aggregate)
- Route file docstring updated with a prominent INTERIM IMPLEMENTATION
  header pointing at the Kensa convergence target and the coordination
  docs

No behavioral changes. No endpoint surface changes. This establishes the
convergence-annotation pattern that Kensa counter-asked for in their
response §6.1, so that future reviewers can grep for stale interim
annotations past their convergence week.
remyluslosius added a commit that referenced this pull request Apr 14, 2026
…#401)

Third and final PR in the Kensa↔OpenWatch coordination batch (PRs #399,
#400, this one). Updates the Q1-Q3 plan to reflect the architectural
commitments made in the 2026-04-14 coordination.

.gitignore:
- Whitelist docs/OPENWATCH_Q1_PLAN.md, Q2_PLAN.md, Q1_Q3_PLAN.md,
  VISION.md, VISION_STATUS.md, and KENSA_OPENWATCH_COORDINATION_*.md
  so cross-team coordination docs can reference them and the Kensa
  convergence schedule is reviewable.

docs/OPENWATCH_Q1_Q3_PLAN.md (rewrite):
- Add top-level Kensa Convergence Addendum documenting:
    - The "GitHub over Kensa's git" posture
    - Section-by-section table of what changes under the addendum
    - What stays purely OpenWatch-layer (SSO, notifications, 6.3, 6.4, 6.5)
    - Kensa milestone convergence table (Week 1, 22, 24, 25, 26, 40)
    - The interim_implementation: frontmatter convention
- Rewrite §6.1 (Transaction Log Query API): endpoint URL + schema
  stable; implementation swaps to kensa.api.Kensa.TransactionLog() at
  Kensa Week 22. First slice shipped as PR #398; annotated in PR #399.
- Rewrite §6.2 (Proactive Remediation Workflow): original draft had
  OpenWatch generating the plan; revised architecture wraps
  Kensa.Plan / Kensa.Execute with an approval UI. Detailed flow
  diagram, ownership split, blocking dependency on Kensa Week 24.
- Update §3.4 (Fleet health): PostgreSQL transactions table reframed
  as derived cache; Kensa.Subscribe feeds drift counts at Week 25.

docs/KENSA_OPENWATCH_COORDINATION_2026-04-14.md (now tracked):
- The outbound coordination memo becomes a first-class committed
  artifact. Already written 2026-04-14; this commit tracks it so the
  Kensa response (at kensa/docs/...) can reference it without
  cross-repo submodule complexity.

docs/OPENWATCH_Q1_PLAN.md, Q2_PLAN.md, VISION.md, VISION_STATUS.md:
- Newly tracked. Earlier session edits (FreeBSD removal markers) are
  already in these local copies and are committed as-is.

No code changes. This is the plan-doc counterpart to PR #399 (query
interim annotation) and PR #400 (signing scope narrow).
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.

2 participants