Skip to content

[integrations] open-brain-rest — Cloudflare Worker REST gateway for the Next.js dashboard#239

Open
tswicegood wants to merge 7 commits intoNateBJones-Projects:mainfrom
tswicegood:contrib/tswicegood/cloudflare-rest-gateway
Open

[integrations] open-brain-rest — Cloudflare Worker REST gateway for the Next.js dashboard#239
tswicegood wants to merge 7 commits intoNateBJones-Projects:mainfrom
tswicegood:contrib/tswicegood/cloudflare-rest-gateway

Conversation

@tswicegood
Copy link
Copy Markdown

@tswicegood tswicegood commented Apr 25, 2026

Contribution Type

  • Integration (/integrations)

Note

Companion PR: #248 — adds Cloudflare Workers deploy support to open-brain-dashboard-next. Together these two PRs deliver the all-Cloudflare deploy story: this Worker as the REST gateway, the dashboard as another Worker, sharing the same MCP_ACCESS_KEY. Either ships independently — this Worker can back a Vercel-hosted dashboard, and #248 works with any open-brain-rest-shaped backend.

What does this do?

Adds integrations/cloudflare-rest-worker/ — a Cloudflare Worker that implements the open-brain-rest REST API the Next.js dashboard expects.

The dashboard's README references open-brain-rest as a prerequisite, but no implementation ships in the repo. This PR fills that gap so the four core dashboard pages (Dashboard, Browse, Detail, Search) work end-to-end against any Open Brain Supabase project.

Endpoint surface

The Worker reverse-engineers its contract from the dashboard's lib/api.ts and app/api/*/route.ts consumer code — no contract was invented:

Method Path Backed by
GET /health unauthenticated; used by login validation
GET /thoughts paginated list with whitelisted sort + filters
GET /thought/:id single-row read
PUT /thought/:id partial update of {content, type, importance, status}
DELETE /thought/:id hard delete
POST /search (semantic) embedding via OpenRouter → existing match_thoughts RPC → re-fetch with sensitivity_tier filter
POST /search (text) existing search_thoughts_text RPC from enhanced-thoughts
GET /stats reshapes existing brain_stats_aggregate RPC into the dashboard's StatsResponse shape
POST /capture metadata extraction + embedding in parallel → existing upsert_thought RPC
GET /ingestion-jobs empty stub (smart-ingest is out of scope; see Known Limitations)
POST /ingest, /ingestion-jobs/:id/execute 501 Not Implemented
GET /ingestion-jobs/:id 404

Total: 10 functional endpoints + 4 deliberate stubs.

Architecture

Browser
   │
   │ HTTPS, iron-session cookie
   ▼
Cloudflare Pages: open-brain-dashboard-next
   │
   │ HTTPS, server-side, x-brain-key from session cookie
   ▼
Cloudflare Worker: open-brain-rest          ← this PR
   │
   │ HTTPS, service-role JWT
   ▼
Supabase (thoughts table + RPCs)

Auth uses the same MCP_ACCESS_KEY already set for open-brain-mcp — no new shared secret. The browser never sees the key (encrypted in iron-session cookie, decrypted server-side in the dashboard's API routes).

Requirements

Existing:

  • Working Open Brain setup (thoughts table + match_thoughts/upsert_thought RPCs from getting-started)
  • schemas/enhanced-thoughts/ applied (provides search_thoughts_text, brain_stats_aggregate, and the type/sensitivity_tier/importance/quality_score/source_type columns)

New:

  • Cloudflare account (free tier)
  • wrangler CLI
  • Node.js 20+

Known limitations (documented in the integration README)

These are real impedance mismatches between the dashboard's expectations and the upstream schema. The Worker is correct as-built; resolving them is out of scope for this PR:

  1. Thought.id: number vs thoughts.id UUID. The dashboard does parseInt(id, 10) on URL params (app/thoughts/[id]/page.tsx:29). UUIDs parse to NaN. The Worker returns UUIDs as strings — Browse and stats render fine; Detail navigation needs the dashboard's id type widened. A follow-up PR can patch the dashboard types.

  2. importance scale. Dashboard's PRIORITY_LEVELS expects 0–100 (Critical = 80+). enhanced-thoughts schema defaults importance to 3. Existing data renders as "Low" priority. Not a Worker bug.

  3. No reflections table. Detail page calls /thought/:id/reflection. No schema creates this table; the Worker doesn't implement the endpoint.

  4. No smart-ingest integration. /ingest, /ingestion-jobs/:id, /ingestion-jobs/:id/execute return 501. Single-thought capture via /capture works.

These don't block the four core pages.

Out of scope (future PRs)

  • Workflow kanban endpoints — needs workflow-status schema active
  • Audit + Duplicates bulk operations
  • Reflections (needs schema first)
  • Smart ingest extract / execute

Checklist

  • I've read CONTRIBUTING.md
  • My contribution has a README.md with prerequisites, step-by-step instructions, and expected outcome
  • My metadata.json has all required fields
  • N/A — no new skill/primitive dependencies
  • I tested this on my own Open Brain instance
  • No credentials, API keys, or secrets are included (wrangler.toml is gitignored; only wrangler.toml.example ships)

Test plan

  • wrangler deploy succeeds against a fresh Cloudflare account
  • curl ${WORKER_URL}/health{"status":"ok",…} without auth
  • curl ${WORKER_URL}/thoughts without x-brain-key → 401
  • curl ${WORKER_URL}/thoughts -H 'x-brain-key: …' → paginated rows
  • POST /search semantic mode returns ranked results with similarity scores
  • POST /search text mode returns rows with rank scores
  • GET /stats?days=7 returns {total_thoughts, window_days, types, top_topics}
  • POST /capture creates a row + writes the embedding (verify via SQL Editor)
  • Dashboard npm run dev with NEXT_PUBLIC_API_URL pointing at the Worker — login + Browse + Search render real data

🤖 Generated with Claude Code

tswicegood and others added 5 commits April 25, 2026 16:18
Adds the directory layout and Hono application shell for a Cloudflare
Worker REST gateway that backs dashboards/open-brain-dashboard-next.
Includes:
  - package.json + tsconfig + wrangler.toml.example + .gitignore
  - src/index.ts with Hono app, CORS middleware, /health pre-auth, and
    a requireApiKey gate for everything else
  - src/lib/{auth,types,supabase,embedding,responses}.ts utilities
  - src/routes/health.ts

Auth pattern (x-brain-key / Authorization: Bearer / ?key=) matches
existing Open Brain integrations. No endpoints implemented yet beyond
/health — subsequent commits add thoughts CRUD, search, stats, and
capture.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the four endpoints the dashboard's lib/api.ts uses to render
the Browse, Detail, and Audit pages:
  GET    /thoughts           — paginated list (page, per_page, type,
                               source_type, importance_min,
                               quality_score_max, sort, order, status,
                               exclude_restricted)
  GET    /thought/:id        — single row, 404 on miss
  PUT    /thought/:id        — partial update of {content, type,
                               importance, status}; updates
                               status_updated_at when status changes
  DELETE /thought/:id        — hard delete, 204 on success, 404 on miss

Sort column is whitelisted (created_at, updated_at, importance,
quality_score, type, source_type, status) to keep arbitrary client
input out of the ORDER BY clause. exclude_restricted defaults true and
filters sensitivity_tier='restricted', matching the convention used by
the brain_stats_aggregate RPC in schemas/enhanced-thoughts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two endpoints used by the dashboard's Search and Dashboard pages:

  POST /search { query, mode, limit, page, exclude_restricted }
    - mode=semantic: embed the query via OpenRouter
      (text-embedding-3-small, same model as open-brain-mcp), call the
      core match_thoughts RPC for candidate IDs + similarity, then
      re-fetch full Thought rows and stitch similarity back. Over-fetches
      3x to absorb sensitivity_tier='restricted' filtering.
    - mode=text: call search_thoughts_text RPC (from
      schemas/enhanced-thoughts), post-filter restricted in JS, slice to
      limit. Uses the RPC's denormalized total_count for pagination.

  GET /stats?days=&exclude_restricted=
    - calls brain_stats_aggregate RPC and reshapes the payload from
      { total, top_types, top_topics } into the dashboard's
      StatsResponse shape ({ total_thoughts, window_days, types,
      top_topics }). days=0 reports window_days='all'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the /capture endpoint that backs the dashboard's "Add to Brain"
single-thought flow, plus stubs for the smart-ingest endpoints:

  POST /capture { content }
    - Runs metadata extraction (gpt-4o-mini JSON mode) and embedding
      (text-embedding-3-small) in parallel via OpenRouter
    - Calls the existing upsert_thought RPC (defined in
      docs/01-getting-started.md Step 2.6) for dedup-aware insertion
    - Backfills embedding + type + source_type='rest-gateway' on the
      returned row
    - Responds with the dashboard's CaptureResult shape:
      { thought_id, action, type, sensitivity_tier,
        content_fingerprint, message }

  GET  /ingestion-jobs            → { jobs: [], count: 0 }
  GET  /ingestion-jobs/:id        → 404
  POST /ingestion-jobs/:id/execute → 501
  POST /ingest                    → 501

The dashboard's Ingest page renders cleanly against an empty list.
The dashboard's Add to Brain extract-mode + execute flows surface 501
errors — documented in the integration README as out-of-scope-for-v1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the integration's README (architecture, prerequisites, 5-step
setup, dashboard wiring, Cloudflare Pages instructions for the Next.js
frontend, known limitations, troubleshooting) and a one-paragraph
pointer in dashboards/open-brain-dashboard-next/README.md so anyone
who lands there can find the missing backend.

Known limitations are documented honestly: Thought.id is UUID (not
number), importance scale mismatch (3 vs 80+), no reflections table,
no smart-ingest. These are upstream concerns out of scope for this PR;
the four core dashboard pages still work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added dashboard Contribution: frontend template integration Contribution: MCP extension or capture source labels Apr 25, 2026
@tswicegood
Copy link
Copy Markdown
Author

Claude got a little ambitious here and opened this draft PR before I've finished it. Was planning on opening it at some point, but will come back to this once I've verified everything. I'm testing it against the dashboard and decided to deploy to Cloudflare since I'm running #238 in production already.

tswicegood and others added 2 commits April 27, 2026 09:15
text-embedding-3-small produces cosine similarities in the ~0.2-0.5 range
for clearly related content; only near-paraphrases climb above 0.5. The
previous 0.5 default starved semantic search of real results — verified
end-to-end against the dashboard, where every query returned zero rows
even though match_thoughts at threshold 0 returned candidates with
similarity 0.31-0.37 to the query.

Drops the constant to 0.2 and exposes it as an optional `threshold` field
on the /search request body so callers with pickier needs can override.

Also adds package-lock.json to .gitignore — wrangler bundles deps at
deploy time, so the lockfile isn't needed for reproducibility and would
just churn the diff.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nnextjs/cloudflare

The previous instructions pointed at @cloudflare/next-on-pages, but that
adapter caps at Next 15.5.x. The dashboards/open-brain-dashboard-next
package ships Next 16, so the documented deploy path was unrunnable.

Updates the "Deploying the Dashboard to Cloudflare" section to use the
@opennextjs/cloudflare adapter (Cloudflare's current path for Next 15+),
which deploys the app as a Worker with static assets, and notes that
NEXT_PUBLIC_API_URL is build-time vs. SESSION_SECRET being runtime —
catching that distinction was the difference between a working deploy
and a half-configured one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dashboard Contribution: frontend template integration Contribution: MCP extension or capture source

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant