Skip to content

Commit 517c875

Browse files
committed
Rename
1 parent ac85b84 commit 517c875

31 files changed

Lines changed: 979 additions & 397 deletions

README.md

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

3-
A versioned, content-addressed registry for knowledge corpora. Apps publish snapshots of their data to Underlay; Underlay preserves them, deduplicates files, and exposes them via a stable HTTPS API.
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.
44

55
**Underlay is the archive underneath your app.**
66

@@ -49,7 +49,7 @@ The seed creates an admin account you can log in with:
4949
- **Email:** admin@underlay.org
5050
- **Password:** admin
5151

52-
It also creates a "Knowledge Futures" org with three sample corpora.
52+
It also creates a "Knowledge Futures" org with three sample collections.
5353

5454
## Architecture
5555

@@ -70,7 +70,7 @@ src/
7070
│ ├── plugins/auth.ts # Authentication (API keys + sessions)
7171
│ ├── routes/ # API route handlers
7272
│ │ ├── accounts.ts # Signup, login, API key CRUD
73-
│ │ ├── corpora.ts # Corpus CRUD
73+
│ │ ├── collections.ts # Collection CRUD
7474
│ │ ├── versions.ts # Version push/pull/diff
7575
│ │ ├── files.ts # Content-addressed file storage
7676
│ │ └── health.ts # Health check
@@ -88,19 +88,19 @@ src/
8888
│ └── s3.ts # S3 client utilities
8989
├── pages/
9090
│ ├── index.astro # Landing page
91-
│ ├── explore.astro # Browse public corpora
91+
│ ├── explore.astro # Browse public collections
9292
│ ├── connect.astro # Integration guide (for devs and LLMs)
9393
│ ├── login.astro # Login form
9494
│ ├── signup.astro # Signup form
95-
│ ├── dashboard.astro # Authenticated user's corpora
95+
│ ├── dashboard.astro # Authenticated user's collections
9696
│ ├── settings/ # Account settings + API key management
9797
│ ├── blog/ # Blog posts
9898
│ ├── docs/ # Documentation (concepts, quickstart, API ref, self-hosting)
9999
│ └── [owner]/ # Dynamic routes
100100
│ ├── index.astro # /:owner — account profile
101-
│ └── [corpus]/
102-
│ ├── index.astro # /:owner/:corpuscorpus overview
103-
│ └── v/[n].astro # /:owner/:corpus/v/:n — version detail
101+
│ └── [collection]/
102+
│ ├── index.astro # /:owner/:collectioncollection overview
103+
│ └── v/[n].astro # /:owner/:collection/v/:n — version detail
104104
├── styles/
105105
│ └── global.css # Tailwind theme (parchment/ink palette)
106106
tools/
@@ -124,33 +124,33 @@ All endpoints are under `/api`. Auth via `Authorization: Bearer <api_key>` for w
124124
| GET | `/api/accounts/keys` | List API keys |
125125
| DELETE | `/api/accounts/keys/:id` | Revoke API key |
126126

127-
### Corpora
127+
### Collections
128128
| Method | Endpoint | Description |
129129
|--------|----------|-------------|
130-
| GET | `/api/corpora` | Browse public corpora |
131-
| POST | `/api/accounts/:owner/corpora` | Create corpus |
132-
| GET | `/api/corpora/:owner/:slug` | Corpus metadata |
133-
| PATCH | `/api/corpora/:owner/:slug` | Update corpus |
134-
| DELETE | `/api/corpora/:owner/:slug` | Delete corpus |
135-
| GET | `/api/accounts/:owner/corpora` | List owner's corpora |
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 |
136136

137137
### Versions
138138
| Method | Endpoint | Description |
139139
|--------|----------|-------------|
140-
| POST | `/api/corpora/:owner/:slug/versions` | Push a version |
141-
| GET | `/api/corpora/:owner/:slug/versions` | List versions |
142-
| GET | `/api/corpora/:owner/:slug/versions/latest` | Latest version |
143-
| GET | `/api/corpora/:owner/:slug/versions/:n` | Get version |
144-
| GET | `/api/corpora/:owner/:slug/versions/:n/records` | Get records |
145-
| GET | `/api/corpora/:owner/:slug/versions/:n/manifest` | Get manifest |
146-
| GET | `/api/corpora/:owner/:slug/versions/:n/diff` | Diff versions |
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 |
147147

148148
### Files
149149
| Method | Endpoint | Description |
150150
|--------|----------|-------------|
151-
| HEAD | `/api/corpora/:owner/:slug/files/:hash` | Check existence |
152-
| GET | `/api/corpora/:owner/:slug/files/:hash` | Download |
153-
| PUT | `/api/corpora/:owner/:slug/files/:hash` | Upload |
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 |
154154

155155
## Scripts
156156

public/.well-known/ai.txt

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ There are two auth methods:
2323
POST /api/accounts/logout clears the session.
2424
GET /api/accounts/me returns the current user (works with either auth method).
2525

26-
Public endpoints (browsing corpora, reading public versions) require no auth.
26+
Public endpoints (browsing collections, reading public versions) require no auth.
2727
Write endpoints require a key with "write" scope or an active session.
2828
Delete endpoints require "admin" scope.
2929

3030
---
3131

3232
## Core Concepts
3333

34-
- Corpus: a named, versioned body of data owned by an account. Identified by :owner/:slug.
34+
- Collection: a named, versioned body of data owned by an account. Identified by :owner/:slug.
3535
- Version: an immutable snapshot containing a JSON Schema, records, and file references. Sequential integer numbers, auto-derived semver.
3636
- Record: a flat JSON object with { id, type, data }. Records reference other records by id and files by hash.
3737
- File: a binary blob stored by SHA-256 hash, referenced in record data as {"$file": "sha256:<hex>"}.
@@ -41,27 +41,27 @@ Delete endpoints require "admin" scope.
4141

4242
## Reading Data
4343

44-
### Browse corpora
45-
GET /api/corpora → list public corpora (?q=search&limit=50&offset=0)
46-
GET /api/corpora/:owner/:slug → corpus metadata + latest version summary
44+
### Browse collections
45+
GET /api/collections → list public collections (?q=search&limit=50&offset=0)
46+
GET /api/collections/:owner/:slug → collection metadata + latest version summary
4747

4848
### Read versions
49-
GET /api/corpora/:owner/:slug/versions → list versions (newest first, ?limit=50&offset=0)
50-
GET /api/corpora/:owner/:slug/versions/latest → latest version with full metadata
51-
GET /api/corpora/:owner/:slug/versions/:n → specific version by number
49+
GET /api/collections/:owner/:slug/versions → list versions (newest first, ?limit=50&offset=0)
50+
GET /api/collections/:owner/:slug/versions/latest → latest version with full metadata
51+
GET /api/collections/:owner/:slug/versions/:n → specific version by number
5252

5353
### Read records and files
54-
GET /api/corpora/:owner/:slug/versions/:n/records → records for a version (?type=TypeName&limit=100&offset=0)
55-
GET /api/corpora/:owner/:slug/versions/:n/manifest → lightweight manifest: record ids/types + file hashes
56-
GET /api/corpora/:owner/:slug/files/:hash → download a file by hash
57-
HEAD /api/corpora/:owner/:slug/files/:hash → check if a file exists (returns Content-Length, Content-Type)
54+
GET /api/collections/:owner/:slug/versions/:n/records → records for a version (?type=TypeName&limit=100&offset=0)
55+
GET /api/collections/:owner/:slug/versions/:n/manifest → lightweight manifest: record ids/types + file hashes
56+
GET /api/collections/:owner/:slug/files/:hash → download a file by hash
57+
HEAD /api/collections/:owner/:slug/files/:hash → check if a file exists (returns Content-Length, Content-Type)
5858

5959
### Diff
60-
GET /api/corpora/:owner/:slug/versions/:n/diff?from=:m → diff between version m and n (added, updated, removed records)
60+
GET /api/collections/:owner/:slug/versions/:n/diff?from=:m → diff between version m and n (added, updated, removed records)
6161

6262
### Export
63-
GET /api/corpora/:owner/:slug/export → download .tar.gz archive (manifest.json + records/*.ndjson + files/*)
64-
GET /api/corpora/:owner/:slug/export?version=3 → export a specific version
63+
GET /api/collections/:owner/:slug/export → download .tar.gz archive (manifest.json + records/*.ndjson + files/*)
64+
GET /api/collections/:owner/:slug/export?version=3 → export a specific version
6565

6666
---
6767

@@ -70,13 +70,13 @@ GET /api/corpora/:owner/:slug/export?version=3 → export a specific versio
7070
This is the canonical workflow for syncing an app's data to Underlay:
7171

7272
### Step 1: Get current state
73-
GET /api/corpora/:owner/:slug/versions/latest
73+
GET /api/collections/:owner/:slug/versions/latest
7474

7575
Response includes { number, semver, hash, recordCount, fileCount }.
7676
If 404, no versions exist yet — your first push should use base_version: null.
7777

7878
### Step 2: Fetch the manifest (optional, for diffing)
79-
GET /api/corpora/:owner/:slug/versions/:n/manifest
79+
GET /api/collections/:owner/:slug/versions/:n/manifest
8080

8181
Response:
8282
{
@@ -92,15 +92,15 @@ Compare this against your local data to determine what changed.
9292
### Step 3: Upload new files (if any)
9393
For each file your app has that Underlay doesn't:
9494

95-
PUT /api/corpora/:owner/:slug/files/sha256:<hex>
95+
PUT /api/collections/:owner/:slug/files/sha256:<hex>
9696
Content-Type: application/octet-stream
9797
Body: raw file bytes
9898

9999
The server verifies the SHA-256 hash matches the body. Existing hashes are idempotent (200 OK).
100100
Check existence first with HEAD if you want to skip uploads.
101101

102102
### Step 4: Push the version
103-
POST /api/corpora/:owner/:slug/versions
103+
POST /api/collections/:owner/:slug/versions
104104
Content-Type: application/json
105105
Authorization: Bearer ul_<key>
106106

@@ -173,7 +173,7 @@ Set base_version to null. Put all records in changes.added. Include the schema.
173173
}
174174
}
175175

176-
- id: stable, unique within the corpus. Use your app's primary key or generate a deterministic one.
176+
- id: stable, unique within the collection. Use your app's primary key or generate a deterministic one.
177177
- type: groups records by kind (e.g. "Article", "Author", "Grant").
178178
- data: flat JSON object. Reference other records by id. Reference files with {"$file": "sha256:<hex>"}.
179179

@@ -191,12 +191,12 @@ The schema declares which fields are references, so tools can resolve them at re
191191

192192
---
193193

194-
## Corpus Management
194+
## Collection Management
195195

196-
POST /api/accounts/:owner/corpora → create corpus {"slug", "name", "description", "public"}
197-
PATCH /api/corpora/:owner/:slug → update {"name", "description", "public"}
198-
DELETE /api/corpora/:owner/:slug → delete corpus (requires admin scope)
199-
GET /api/accounts/:owner/corpora → list corpora for an account
196+
POST /api/accounts/:owner/collections → create collection {"slug", "name", "description", "public"}
197+
PATCH /api/collections/:owner/:slug → update {"name", "description", "public"}
198+
DELETE /api/collections/:owner/:slug → delete collection (requires admin scope)
199+
GET /api/accounts/:owner/collections → list collections for an account
200200

201201
---
202202

@@ -214,7 +214,7 @@ DELETE /api/accounts/keys/:id → revoke a key
214214
400 — Validation error (bad request body)
215215
401 — Authentication required
216216
403 — Insufficient scope (e.g. read key used for write)
217-
404 — Not found (or private corpus you can't access)
217+
404 — Not found (or private collection you can't access)
218218
409 — Version conflict (re-fetch and retry with new base_version)
219219
422 — Missing files (upload them first, then retry push)
220220

src/api/plugins/auth.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ declare module "fastify" {
88
interface FastifyRequest {
99
accountId?: string;
1010
apiKeyScope?: "read" | "write" | "admin";
11-
apiKeyCorpusId?: string | null;
11+
apiKeyCollectionId?: string | null;
1212
sessionUserId?: string;
1313
}
1414
}
@@ -17,7 +17,7 @@ async function authPlugin(app: FastifyInstance) {
1717
// API key auth via Bearer token
1818
app.decorateRequest("accountId", undefined);
1919
app.decorateRequest("apiKeyScope", undefined);
20-
app.decorateRequest("apiKeyCorpusId", undefined);
20+
app.decorateRequest("apiKeyCollectionId", undefined);
2121
app.decorateRequest("sessionUserId", undefined);
2222

2323
app.addHook("onRequest", async (request: FastifyRequest) => {
@@ -30,7 +30,7 @@ async function authPlugin(app: FastifyInstance) {
3030
if (match) {
3131
request.accountId = key.accountId;
3232
request.apiKeyScope = key.scope as "read" | "write" | "admin";
33-
request.apiKeyCorpusId = key.corpusId;
33+
request.apiKeyCollectionId = key.collectionId;
3434
await db
3535
.update(schema.apiKeys)
3636
.set({ lastUsedAt: new Date() })

0 commit comments

Comments
 (0)