Skip to main content

Cloudflare Worker API

The React SPA talks to a Cloudflare Worker that authenticates requests and proxies named parameterised queries to ClickHouse.

Architecture

React SPA (served by Worker as static assets)
|
v
Cloudflare Worker (components/api/)
| URL-pattern routing -> query builder functions -> parameterised SQL
|
v
ClickHouse (HTTPS, parameterised queries)

Named queries live in the Worker, not the client. Query builder functions in src/queries/ (cost.ts, resources.ts, hierarchy.ts) return {sql, params} objects. The router calls these and forwards to ClickHouse. No raw SQL from the client ever reaches ClickHouse.

Authentication

Cloudflare Access JWT validation (src/auth.ts):

  • Validates JWTs from cf-access-token header or CF_Authorization cookie
  • Verifies against team JWKS endpoint
  • Validates issuer, audience, and email claim
  • Health endpoint (/api/v1/health) requires no auth

Endpoints

Implemented

Cost

GET /api/v1/cost/by-customer ?days=30
GET /api/v1/cost/by-account-role ?days=30
GET /api/v1/cost/tree/:nodeId ?days=30
GET /api/v1/cost/trend/:nodeId ?days=90&granularity=day|hour

Resources

GET /api/v1/resources ?type=ec2:instance&state=running&account_role=...&customer=...&limit=100
GET /api/v1/resources/:arn
GET /api/v1/resources/:arn/children
GET /api/v1/resources/:arn/ancestors

Hierarchy

GET /api/v1/hierarchy/tree ?hierarchy=marqo_logical&root=...
GET /api/v1/hierarchy/node/:nodeId

System

GET /api/v1/health

Delta Decomposition

GET /api/v1/cost/delta ?currentStart=...&currentEnd=...&compareStart=...&compareEnd=...&hierarchy=...&parentNodeId=...
GET /api/v1/cost/delta/resources ?nodeId=...&currentStart=...&currentEnd=...&compareStart=...&compareEnd=...
GET /api/v1/cost/delta/events ?arn=...&start=...&end=...

Planned — Not Yet Implemented

Cost (additional)

GET /api/v1/cost/allocated/:nodeId ?days=30
GET /api/v1/forecast/:nodeId ?month=2025-04

Actions

POST /api/v1/actions/preview { suggestion_id }
POST /api/v1/actions/execute { suggestion_id, confirmed: true }
POST /api/v1/actions/dismiss { suggestion_id, reason }
GET /api/v1/actions/suggestions ?status=pending&sort=saving
GET /api/v1/actions/log ?days=30
GET /api/v1/actions/savings ?days=90

Governance

GET /api/v1/rules
POST /api/v1/rules
GET /api/v1/rules/violations ?severity=critical&status=open
GET /api/v1/anomalies ?days=30
GET /api/v1/changelog ?date=2025-03-28

System (additional)

GET /api/v1/system/status # collector health, account coverage

ClickHouse Client (src/clickhouse.ts)

  • HTTPS POST to ClickHouse with basic auth
  • ClickHouse native {param:Type} parameterisation
  • 5s default timeout
  • JSONEachRow response format
  • Connection credentials from Wrangler secrets

Action Execution

Status: PLANNED — No POST endpoints or Lambda invocation logic exists yet.

The /api/v1/actions/execute endpoint will invoke the Action Lambda via AWS SDK (Lambda.invoke). The Worker will have an AWS API key for Lambda invocation only — not for EC2/EBS/etc. The Lambda will have its own polo-action-role.

Error Handling

All endpoints return consistent JSON error format:

{ "error": "message", "code": "NOT_FOUND" }

HTTP status codes: 200 (success), 400 (bad params), 401 (no auth), 403 (forbidden), 404 (not found), 500 (internal).