Engineering

How closedNote works

Technical overview of the architecture, features, and design decisions behind closedNote — the only prompt manager that tracks how your prompts evolve.

S

Samuel Aboderin

Computer Engineering · UNILAG · v1.1

Why closedNote?

PromptBase stores prompts. Notion organizes them. FlowGPT shares them. None of them remember how they got there.

In real life, prompts evolve. You tweak your "code review prompt" three times, and by the fourth iteration you've forgotten what made version 2 actually work. closedNote is built on one thesis: a prompt is not a sticky note — it's a document with a history.

Beyond versioning, closedNote adds structure: prompts organized into collections, chained into multi-step workflows, refined by AI, and importable from any image via OCR — all private by default, all in one place.


Version History

new

Every time a user saves an edit to a prompt, closedNote snapshots it into a prompt_versions table. A version history panel on the prompt detail page shows the full timeline. Clicking any version renders a live diff against the current content using Google's diff-match-patch library — additions in green, removals in red.

Restoring a version updates the prompt content without creating a new version entry — preserving the history chain exactly as it was. A new version is only created when the user edits and saves content that differs from the last saved state.

BehaviourCreates new version?
Save with changed content or titleYes
Save with no changesNo
Restore a previous versionNo
Edit after restore, content differsYes

Key files

  • supabase/migrations/004_prompt_versions.sql- table + RLS policies
  • lib/promptData.ts- savePrompt() with skipVersion flag
  • app/api/prompts/[id]/versions/route.ts- authenticated GET endpoint
  • components/VersionHistory.tsx- timeline + diff UI

Architecture

closedNote is a Next.js 14 App Router application with a Supabase backend. Almost all pages are client components; only the docs page is server-rendered.

Frontend

  • Next.js 14 — App Router, RSC
  • React 18 — hooks, client components
  • Tailwind CSS 3.4 — utility-first styling
  • TypeScript 5.5 — full type safety
  • diff-match-patch — version diff engine

Backend

  • Supabase — PostgreSQL + Auth
  • PKCE flow — secure auth
  • Row Level Security — per-user isolation
  • Vercel — edge deployment

Key files

  • lib/hooks/usePrompts.ts- data fetching hook
  • lib/promptData.ts- prompt CRUD + versioning
  • lib/chainData.ts- chain CRUD
  • components/AuthProvider.tsx- auth context
  • components/VersionHistory.tsx- version timeline + diff
  • app/api/chat/route.ts- AI refinement proxy
  • app/api/ocr/route.ts- OCR endpoint
  • app/api/prompts/[id]/versions/route.ts- version history endpoint

OCR & AI Refinement

Upload a screenshot, photo, or scan — GPT-4o Vision extracts the text, and the AI refinement step restructures it into a clean, reusable prompt. A Tesseract.js fallback handles offline cases.

Online (GPT-4o Vision)

  • Printed & handwritten text
  • High accuracy on complex layouts
  • Requires an OpenAI API key

Offline (Tesseract.js)

  • Runs entirely in the browser
  • No API key required
  • Works offline and on flights

The app attempts the online route first and silently falls back to Tesseract if the API is unavailable or unconfigured.


AI Provider

Chain runs and chat refinement use HuggingFace's Zephyr-7B by default — free, no billing required. If you add your own OpenAI key in Settings, it takes priority and unlocks GPT-4o Mini for all AI features.

# .env.local

NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co# required

NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...# required

OPENAI_API_KEY=sk-...# optional — enables GPT-4o OCR + AI

HUGGINGFACE_API_KEY=hf_...# optional — free AI fallback


Database

Six tables in PostgreSQL via Supabase. All have RLS enabled — every query is automatically scoped to the authenticated user.

TablePurpose
usersAuth profile, synced from Supabase Auth on signup
promptsTitle, content, model, collection, user_id
prompt_versionsnewVersioned snapshots of each prompt — powers the diff view
tagsMany-to-many; cascade-deletes with prompt
prompt_chainsTitled sequences of steps, owned by user
chain_stepsOrdered steps with content and output variables

Security

  • Row Level Security

    All tables enforce RLS. Every query is automatically filtered to the authenticated user's data at the database level — including prompt_versions.

  • PKCE Auth Flow

    Supabase Auth uses PKCE (Proof Key for Code Exchange). Sessions are stored ephemerally — cleared when the browser closes.

  • API key privacy

    User-supplied OpenAI keys are stored only in localStorage and passed per-request. They are never persisted server-side.

  • Authenticated API routes

    The /api/prompts/[id]/versions endpoint validates the user's JWT before any query. No RLS bypass is possible.

  • HTTPS

    All traffic is encrypted in transit. Enforced by Vercel on production.


Deployment

Optimised for Vercel, but runs on any platform that supports Next.js 14.

  1. 1

    Fork the repo

    Clone or fork from GitHub: github.com/aboderinsamuel/closedNote

  2. 2

    Create a Supabase project

    Run all four migration files in order from supabase/migrations/ via the Supabase SQL Editor. The fourth migration (004_prompt_versions.sql) creates the version history table.

  3. 3

    Set environment variables

    Add NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY. Optionally add OPENAI_API_KEY for GPT-4o OCR.

  4. 4

    Deploy to Vercel

    Connect the repo, add the env vars, and deploy. Update your Supabase Auth redirect URLs to match your production domain.


Contributing

closedNote is open source. Whether you're fixing a typo or building a feature, contributions are welcome.

AI-powered tag suggestions
Team sharing & collaboration
Export to PDF / Markdown
Browser extension
Pagination & infinite scroll
Prompt templates with variables

Built by Samuel Aboderin · Computer Engineering · UNILAG