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 <noreply@anthropic.com>
This commit is contained in:
38
SPEC.md
38
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
|
||||
|
||||
|
||||
127
doc/DATABASE.md
Normal file
127
doc/DATABASE.md
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user