From 54f35262529b221d8d9f65146994e6a4c5dab8ce Mon Sep 17 00:00:00 2001 From: Forgotten Date: Mon, 16 Feb 2026 20:21:15 -0600 Subject: [PATCH] Add database setup guide and clean up spec formatting Add doc/DATABASE.md documenting the three database modes: embedded PGlite, local Docker PostgreSQL, and hosted production. Fix markdown table alignment and minor whitespace in SPEC.md. Co-Authored-By: Claude Opus 4.6 --- SPEC.md | 38 ++++++++------- doc/DATABASE.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 18 deletions(-) create mode 100644 doc/DATABASE.md diff --git a/SPEC.md b/SPEC.md index e4eb0507..a43379c5 100644 --- a/SPEC.md +++ b/SPEC.md @@ -10,13 +10,13 @@ A company is a first-order object. One Paperclip instance runs multiple companie ### Fields (Draft) -| Field | Type | Notes | -| --- | --- | --- | -| `id` | uuid | Primary key | -| `name` | string | Company name | -| `goal` | text/markdown | The company's top-level objective | -| `createdAt` | timestamp | | -| `updatedAt` | timestamp | | +| Field | Type | Notes | +| ----------- | ------------- | --------------------------------- | +| `id` | uuid | Primary key | +| `name` | string | Company name | +| `goal` | text/markdown | The company's top-level objective | +| `createdAt` | timestamp | | +| `updatedAt` | timestamp | | ### Board Governance [DRAFT] @@ -42,6 +42,7 @@ The board has **unrestricted access** to the entire system at all times: The board is not just an approval gate — it's a live control surface. The human can intervene at any level at any time. **Future governance models** (not V1): + - Hiring budgets (auto-approve hires within $X/month) - Multi-member boards - Delegated authority (CEO can hire within limits) @@ -104,7 +105,7 @@ Configurable per agent. Two ends of the spectrum: #### Minimum Contract -The minimum requirement to be a Paperclip agent: **be callable.** That's it. Paperclip can invoke you via command or webhook. No requirement to report back — Paperclip infers basic status from process liveness. +The minimum requirement to be a Paperclip agent: **be callable.** That's it. Paperclip can invoke you via command or webhook. No requirement to report back — Paperclip infers basic status from process liveness when it can. #### Integration Levels @@ -182,10 +183,10 @@ The heartbeat is a protocol, not a runtime. Paperclip defines how to initiate an Agent configuration includes an **adapter** that defines how Paperclip invokes the agent. Initial adapters: -| Adapter | Mechanism | Example | -| --- | --- | --- | -| `process` | Execute a child process | `python run_agent.py --agent-id {id}` | -| `http` | Send an HTTP request | `POST https://openclaw.example.com/hook/{id}` | +| Adapter | Mechanism | Example | +| --------- | ----------------------- | --------------------------------------------- | +| `process` | Execute a child process | `python run_agent.py --agent-id {id}` | +| `http` | Send an HTTP request | `POST https://openclaw.example.com/hook/{id}` | More adapters can be added. @@ -355,6 +356,7 @@ The key constraint: it must be trivial to go from "I'm trying this on my machine Agents need to register and authenticate with the Paperclip server to get an API key that identifies them. Flow: + 1. Agent "signs up" — requests access to the Paperclip instance 2. Human board member approves/onboards the agent 3. Agent receives credentials (API key) and can now interact with the control plane @@ -363,12 +365,12 @@ This is the same pattern as agent hiring — an agent can't just show up, it nee ### Tech Stack -| Layer | Technology | -| --- | --- | -| Frontend | React + Vite | -| Backend | TypeScript + Hono (REST API, not tRPC — need non-TS clients) | -| Database | PostgreSQL (embedded for dev, hosted for production) | -| Auth | Standard React auth library (not Supabase-dependent) | +| Layer | Technology | +| -------- | ------------------------------------------------------------ | +| Frontend | React + Vite | +| Backend | TypeScript + Hono (REST API, not tRPC — need non-TS clients) | +| Database | PostgreSQL (embedded for dev, hosted for production) | +| Auth | Standard React auth library (not Supabase-dependent) | ### Concurrency Model: Atomic Task Checkout diff --git a/doc/DATABASE.md b/doc/DATABASE.md new file mode 100644 index 00000000..5a91f0be --- /dev/null +++ b/doc/DATABASE.md @@ -0,0 +1,127 @@ +# Database + +Paperclip uses PostgreSQL via [Drizzle ORM](https://orm.drizzle.team/). There are three ways to run the database, from simplest to most production-ready. + +## 1. Embedded (PGlite) — zero config + +If you don't set `DATABASE_URL`, the server automatically starts an embedded PostgreSQL instance powered by [PGlite](https://pglite.dev/). No installation, no Docker, no setup. + +```sh +pnpm dev +``` + +That's it. On first start the server: + +1. Creates a `./data/pglite/` directory for storage +2. Pushes the Drizzle schema to create all tables +3. Starts serving requests + +Data persists across restarts in the `./data/pglite/` directory. To reset the database, delete that directory. + +**Limitations:** + +- Single connection only (no concurrent access from multiple processes) +- Slower than native PostgreSQL (runs in WebAssembly) +- Not suitable for production + +This mode is ideal for getting started, local development, and demos. + +## 2. Local PostgreSQL (Docker) + +For a full PostgreSQL server locally, use the included Docker Compose setup: + +```sh +docker compose up -d +``` + +This starts PostgreSQL 17 on `localhost:5432`. Then set the connection string: + +```sh +cp .env.example .env +# .env already contains: +# DATABASE_URL=postgres://paperclip:paperclip@localhost:5432/paperclip +``` + +Run migrations (once the migration generation issue is fixed) or use `drizzle-kit push`: + +```sh +DATABASE_URL=postgres://paperclip:paperclip@localhost:5432/paperclip \ + npx drizzle-kit push +``` + +Start the server: + +```sh +pnpm dev +``` + +## 3. Hosted PostgreSQL (Supabase) + +For production, use a hosted PostgreSQL provider. [Supabase](https://supabase.com/) is a good option with a free tier. + +### Setup + +1. Create a project at [database.new](https://database.new) +2. Go to **Project Settings > Database > Connection string** +3. Copy the URI and replace the password placeholder with your database password + +### Connection string + +Supabase offers two connection modes: + +**Direct connection** (port 5432) — use for migrations and one-off scripts: + +``` +postgres://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres +``` + +**Connection pooling via Supavisor** (port 6543) — use for the application: + +``` +postgres://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres +``` + +### Configure + +Set `DATABASE_URL` in your `.env`: + +```sh +DATABASE_URL=postgres://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres +``` + +If using connection pooling (port 6543), the `postgres` client must disable prepared statements. Update `packages/db/src/client.ts`: + +```ts +export function createDb(url: string) { + const sql = postgres(url, { prepare: false }); + return drizzlePg(sql, { schema }); +} +``` + +### Push the schema + +```sh +# Use the direct connection (port 5432) for schema changes +DATABASE_URL=postgres://postgres.[PROJECT-REF]:[PASSWORD]@...5432/postgres \ + npx drizzle-kit push +``` + +### Free tier limits + +- 500 MB database storage +- 200 concurrent connections +- Projects pause after 1 week of inactivity + +See [Supabase pricing](https://supabase.com/pricing) for current details. + +## Switching between modes + +The database mode is controlled entirely by the `DATABASE_URL` environment variable: + +| `DATABASE_URL` | Mode | +|---|---| +| Not set | Embedded PGlite (`./data/pglite/`) | +| `postgres://...localhost...` | Local Docker PostgreSQL | +| `postgres://...supabase.com...` | Hosted Supabase | + +Your Drizzle schema (`packages/db/src/schema/`) stays the same regardless of mode.