Skip to content

Commit a85684e

Browse files
committed
Cleanup old scripts and documentaiton
1 parent 898c6ba commit a85684e

16 files changed

Lines changed: 238 additions & 2054 deletions

File tree

.dockerignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
_scriptsRepo
3+
planning
4+
.git
5+
*.md
6+
.env*

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ planning/
99
*.db
1010
*.db-journal
1111
*.db-wal
12+
_scriptsRepo/

README.md

Lines changed: 115 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Underlay
22

3-
A versioned, content-addressed registry for knowledge collections. Apps publish snapshots of their data to Underlay; Underlay preserves them, deduplicates files, and exposes them via a stable HTTPS API.
4-
5-
**Underlay is the archive underneath your app.**
3+
A versioned, content-addressed registry for structured knowledge. Apps publish snapshots of their data to Underlay; Underlay preserves them, deduplicates files, and exposes them via a stable HTTPS API.
64

75
Built by [Knowledge Futures](https://www.knowledgefutures.org), a 501(c)(3) nonprofit.
86

@@ -16,181 +14,137 @@ Built by [Knowledge Futures](https://www.knowledgefutures.org), a 501(c)(3) nonp
1614
### Development
1715

1816
```bash
19-
# Clone and start everything (Postgres, MinIO, app)
2017
git clone https://github.com/knowledgefutures/underlay.git
2118
cd underlay
2219
./dev.sh
2320
```
2421

2522
This starts:
26-
- **PostgreSQL 16** on port 5432
23+
- **PostgreSQL 16** on port 5433 (host) → 5432 (container)
2724
- **MinIO** (S3-compatible storage) on ports 9000/9001
28-
- **Underlay** on port 4321 (frontend) and port 3000 (API)
25+
- **Underlay** on port 4321 (Astro SSR) and port 3000 (Fastify API)
2926

30-
The dev script auto-creates `.env.local` from defaults if one doesn't exist.
27+
The dev script auto-creates `.env.local` from `.env.test` defaults if one doesn't exist.
3128

3229
### Without Docker
3330

3431
```bash
3532
npm install
36-
37-
# Set up your own Postgres and S3, then:
3833
cp .env.test .env.local
39-
# Edit .env.local with your connection strings
40-
34+
# Edit .env.local with your Postgres and S3 connection strings
4135
npm run db:migrate
4236
npm run db:seed
4337
npm run dev:server
4438
```
4539

4640
### Default Seed User
4741

48-
The seed creates an admin account you can log in with:
4942
- **Email:** admin@underlay.org
5043
- **Password:** admin
5144

52-
It also creates a "Knowledge Futures" org with three sample collections.
45+
Also creates a "Knowledge Futures" org with sample collections.
5346

5447
## Architecture
5548

5649
| Layer | Technology |
5750
|-------|-----------|
5851
| Frontend | Astro 6 SSR + React 19 islands + Tailwind CSS 4 |
59-
| API | Fastify 5 (TypeScript) |
52+
| API | Fastify 5 (TypeScript), always binds to port 3000 |
6053
| Database | PostgreSQL 16 + Drizzle ORM |
61-
| File Storage | S3-compatible (AWS S3 / MinIO / R2) |
54+
| File Storage | Cloudflare R2 (prod) / MinIO (dev) — S3-compatible |
6255
| Auth | Session cookies (web) + API keys (programmatic) |
63-
| Deployment | Docker (Alpine), multi-stage build |
56+
| Deployment | Docker Swarm on Hetzner, Caddy reverse proxy, Cloudflare DNS |
57+
| CI/CD | GitHub Actions → GHCR → SSH → `docker stack deploy` |
58+
| Secrets | SOPS + age encryption |
6459

6560
## Project Structure
6661

6762
```
6863
src/
69-
├── api/ # Fastify API server
70-
│ ├── plugins/auth.ts # Authentication (API keys + sessions)
71-
│ ├── routes/ # API route handlers
72-
│ │ ├── accounts.ts # Signup, login, API key CRUD
64+
├── api/ # Fastify API server (port 3000)
65+
│ ├── plugins/auth.ts # Auth (API keys + session cookies)
66+
│ ├── routes/
67+
│ │ ├── accounts.ts # Signup, login, API key CRUD, orgs
7368
│ │ ├── collections.ts # Collection CRUD
74-
│ │ ├── versions.ts # Version push/pull/diff
69+
│ │ ├── versions.ts # Version push/pull/diff + privacy filtering
7570
│ │ ├── files.ts # Content-addressed file storage
7671
│ │ └── health.ts # Health check
7772
│ └── server.ts # Fastify entry point
7873
├── db/
7974
│ ├── schema.ts # Drizzle table definitions
8075
│ ├── index.ts # Database client
81-
│ ├── migrate.ts # Migration runner
82-
│ ├── seed.ts # Seed data (--force to re-seed)
76+
│ ├── migrate.ts # Migration runner (retries on DNS failures)
77+
│ ├── seed.ts # Seed data
8378
│ └── migrations/ # Generated SQL migrations
84-
├── layouts/
85-
│ ├── Base.astro # Root HTML layout
86-
│ └── Docs.astro # Documentation page layout
79+
├── layouts/ # Astro layouts (Base, Docs, BlogPost)
80+
├── components/ # React islands + Astro components
8781
├── lib/
88-
│ └── s3.ts # S3 client utilities
82+
│ ├── s3.ts # S3 client (upload, download, head, list, delete)
83+
│ └── page-utils.ts # SSR utilities
8984
├── pages/
9085
│ ├── index.astro # Landing page
9186
│ ├── explore.astro # Browse public collections
92-
│ ├── connect.astro # Integration guide (for devs and LLMs)
93-
│ ├── login.astro # Login form
94-
│ ├── signup.astro # Signup form
95-
│ ├── dashboard.astro # Authenticated user's collections
87+
│ ├── login/signup.astro
88+
│ ├── dashboard.astro # User's collections
9689
│ ├── settings/ # Account settings + API key management
97-
│ ├── blog/ # Blog posts
90+
│ ├── blog/ # Markdown blog posts
9891
│ ├── docs/ # Documentation (concepts, quickstart, API ref, self-hosting)
9992
│ └── [owner]/ # Dynamic routes
100-
│ ├── index.astro # /:owner — account profile
93+
│ ├── index.astro # Profile page
94+
│ ├── settings.astro # Account/org settings
10195
│ └── [collection]/
102-
│ ├── index.astro # /:owner/:collection — collection overview
103-
│ └── v/[n].astro # /:owner/:collection/v/:n — version detail
104-
├── styles/
105-
│ └── global.css # Tailwind theme (parchment/ink palette)
96+
│ ├── index.astro # Collection overview
97+
│ ├── versions.astro # Version history
98+
│ ├── diff.astro # Version diff viewer
99+
│ ├── settings.astro # Collection settings
100+
│ └── v/[n].astro # Version detail
101+
├── styles/global.css # Tailwind theme (parchment/ink palette)
102+
public/
103+
├── .well-known/ai.txt # Machine-readable API docs (for LLMs/bots)
106104
tools/
107-
├── backupDb.ts # Postgres backup → S3
108-
└── cron.ts # Scheduled task runner
105+
├── backupDb.ts # Postgres backup → S3 (_backups/ prefix)
106+
└── cron.ts # Scheduled task runner (daily backups)
109107
```
110108

111-
## API
112-
113-
All endpoints are under `/api`. Auth via `Authorization: Bearer <api_key>` for writes.
114-
115-
### Accounts
116-
| Method | Endpoint | Description |
117-
|--------|----------|-------------|
118-
| POST | `/api/accounts/signup` | Create account |
119-
| POST | `/api/accounts/login` | Log in (sets session cookie) |
120-
| POST | `/api/accounts/logout` | Log out |
121-
| GET | `/api/accounts/me` | Current user |
122-
| GET | `/api/accounts/:slug` | Public profile |
123-
| POST | `/api/accounts/keys` | Create API key |
124-
| GET | `/api/accounts/keys` | List API keys |
125-
| DELETE | `/api/accounts/keys/:id` | Revoke API key |
126-
127-
### Collections
128-
| Method | Endpoint | Description |
129-
|--------|----------|-------------|
130-
| GET | `/api/collections` | Browse public collections |
131-
| POST | `/api/accounts/:owner/collections` | Create collection |
132-
| GET | `/api/collections/:owner/:slug` | Collection metadata |
133-
| PATCH | `/api/collections/:owner/:slug` | Update collection |
134-
| DELETE | `/api/collections/:owner/:slug` | Delete collection |
135-
| GET | `/api/accounts/:owner/collections` | List owner's collections |
136-
137-
### Versions
138-
| Method | Endpoint | Description |
139-
|--------|----------|-------------|
140-
| POST | `/api/collections/:owner/:slug/versions` | Push a version |
141-
| GET | `/api/collections/:owner/:slug/versions` | List versions |
142-
| GET | `/api/collections/:owner/:slug/versions/latest` | Latest version |
143-
| GET | `/api/collections/:owner/:slug/versions/:n` | Get version |
144-
| GET | `/api/collections/:owner/:slug/versions/:n/records` | Get records |
145-
| GET | `/api/collections/:owner/:slug/versions/:n/manifest` | Get manifest |
146-
| GET | `/api/collections/:owner/:slug/versions/:n/diff` | Diff versions |
147-
148-
### Files
149-
| Method | Endpoint | Description |
150-
|--------|----------|-------------|
151-
| HEAD | `/api/collections/:owner/:slug/files/:hash` | Check existence |
152-
| GET | `/api/collections/:owner/:slug/files/:hash` | Download |
153-
| PUT | `/api/collections/:owner/:slug/files/:hash` | Upload |
109+
## Deployment
154110

155-
## Scripts
111+
### Infrastructure
156112

157-
```bash
158-
npm run dev # Start full dev environment (Docker)
159-
npm run dev:server # Start Astro + API (no Docker)
160-
npm run build # Build for production
161-
npm run start # Start production server
113+
- **Hetzner** — Single box (8 vCPU, 16GB RAM) running Docker Swarm
114+
- **Caddy** — Host-level reverse proxy, TLS via `tls internal` (Cloudflare Full mode)
115+
- **Cloudflare** — DNS + CDN + DDoS protection
116+
- **R2** — Object storage (zero egress fees), single bucket with prefixes:
117+
- `files/` — Content-addressed immutable uploads
118+
- `_backups/` — Compressed Postgres dumps
162119

163-
npm run db:generate # Generate Drizzle migrations
164-
npm run db:migrate # Run migrations
165-
npm run db:seed # Seed database (--force to re-seed)
120+
### Stacks
166121

167-
npm run tool:backup # Manual database backup to S3
122+
Two Docker Swarm stacks run on the same box:
168123

169-
npm run secrets:encrypt # Encrypt .env with SOPS
170-
npm run secrets:decrypt # Decrypt .env.enc with SOPS
171-
```
124+
| Stack | Domain | Host Ports | Purpose |
125+
|-------|--------|-----------|---------|
126+
| `underlay-prod` | www.underlay.org | 4322 (SSR), 3001 (API) | Production |
127+
| `underlay-dev` | dev.underlay.org | 4321 (SSR), 3000 (API) | Staging |
172128

173-
## Deployment
129+
Container-internal ports are always fixed: 4321 (Astro) and 3000 (Fastify).
130+
Host ports are configured via `APP_PORT` and `API_PORT` in .env files (compose-only variables).
174131

175-
### Docker
132+
### CI/CD Flow
176133

177-
```bash
178-
docker build -t underlay .
179-
docker compose up -d
180-
```
134+
1. Push to `main` → deploys to `dev.underlay.org`
135+
2. Create a release/tag → deploys to `www.underlay.org`
136+
3. Manual dispatch → choose environment
181137

182-
The production `docker-compose.yml` runs three services:
183-
- **postgres** — PostgreSQL 16 with persistent volume
184-
- **app** — Migrations + Astro SSR + Fastify API
185-
- **cron** — Scheduled database backups
138+
The workflow: build Docker image → push to GHCR → SSH to server → decrypt secrets → `docker stack deploy` → wait for healthy rollout.
186139

187-
### CI/CD
140+
Required GitHub secrets: `SSH_PRIVATE_KEY`, `SSH_HOST_DEV`, `SSH_HOST_PROD`, `SSH_USER`, `GHCR_USER`, `GHCR_TOKEN`.
188141

189-
Push to `main` triggers the GitHub Actions workflow:
190-
1. Build Docker image → push to GHCR
191-
2. SSH to server → pull → decrypt secrets → `docker compose up`
142+
### Docker Compose Files
192143

193-
Required GitHub secrets: `SSH_PRIVATE_KEY`, `SSH_HOST`, `SSH_USER`, `GHCR_USER`, `GHCR_TOKEN`.
144+
| File | Purpose |
145+
|------|---------|
146+
| `docker-compose.yml` | Deployed stacks (prod & dev via Swarm) |
147+
| `docker-compose.local.yml` | Local development (source-mounted, MinIO, hot reload) |
194148

195149
## Environment Variables
196150

@@ -201,11 +155,60 @@ Required GitHub secrets: `SSH_PRIVATE_KEY`, `SSH_HOST`, `SSH_USER`, `GHCR_USER`,
201155
| `APP_PORT` | Host-published port for Astro SSR (compose only, default: 4322) |
202156
| `API_PORT` | Host-published port for Fastify API (compose only, default: 3001) |
203157
| `S3_BUCKET` | S3 bucket name |
204-
| `S3_REGION` | S3 region |
205-
| `S3_ENDPOINT` | S3 endpoint (for MinIO, R2, etc.) |
158+
| `S3_REGION` | S3 region (`auto` for R2) |
159+
| `S3_ENDPOINT` | S3 endpoint URL |
206160
| `S3_ACCESS_KEY` | S3 access key |
207161
| `S3_SECRET_KEY` | S3 secret key |
208162

163+
`NODE_ENV` is set in `docker-compose.yml` `environment:` block (not in .env files).
164+
165+
## Scripts
166+
167+
```bash
168+
# Development
169+
npm run dev # Start full local stack (Docker)
170+
npm run dev:server # Start Astro + API without Docker
171+
npm run build # Build for production
172+
173+
# Database
174+
npm run db:generate # Generate Drizzle migrations from schema changes
175+
npm run db:migrate # Run pending migrations
176+
npm run db:seed # Seed database
177+
178+
# Tools
179+
npm run tool:backup # Manual database backup to S3
180+
181+
# Secrets (SOPS + age)
182+
npm run secrets:encrypt # Encrypt .env → .env.enc
183+
npm run secrets:encrypt:dev # Encrypt .env.dev → .env.dev.enc
184+
npm run secrets:decrypt # Decrypt .env.enc → .env
185+
npm run secrets:decrypt:dev # Decrypt .env.dev.enc → .env.dev
186+
```
187+
188+
## Maintenance Checklist
189+
190+
When adding or changing features, update these locations:
191+
192+
| What | Where | Purpose |
193+
|------|-------|---------|
194+
| API documentation | `public/.well-known/ai.txt` | Machine-readable docs for LLMs and bots |
195+
| Concepts | `src/pages/docs/concepts.astro` | Core concepts explanation |
196+
| API reference | `src/pages/docs/api/*.astro` | Endpoint-level docs with examples |
197+
| Integration guide | `src/pages/docs/integration.astro` | Developer onboarding guide |
198+
| Quick start | `src/pages/docs/quickstart.astro` | Getting started tutorial |
199+
| Self-hosting | `src/pages/docs/self-host.astro` | Deployment instructions |
200+
| DB schema | `src/db/schema.ts``npm run db:generate` | Schema changes need a migration |
201+
| Encrypted secrets | `.env.enc` / `.env.dev.enc` | Re-encrypt after changing .env files |
202+
203+
### Privacy features
204+
205+
The system supports three levels of privacy (type-level, field-level, record-level) via `"private": true` annotations. When changing how privacy works, update:
206+
- `src/api/routes/versions.ts` — filtering logic
207+
- `src/api/routes/files.ts` — file access checks
208+
- `public/.well-known/ai.txt` — Privacy section
209+
- `src/pages/docs/concepts.astro` — Privacy section
210+
- `src/pages/docs/api/versions.astro` — Push endpoint docs
211+
209212
## License
210213

211214
MIT

0 commit comments

Comments
 (0)