Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
wisp.place
1The project is wisp.place. It is a static site hoster built on top of the AT Protocol. The overall basis of the project is that users upload site assets to their PDS as blobs, and creates a manifest record listing every blob as well as site name. The hosting service then catches events relating to the site (create, read, upload, delete) and handles them appropriately.
2
3The lexicons look like this:
4```typescript
5//place.wisp.fs
6interface Main {
7 $type: 'place.wisp.fs'
8 site: string
9 root: Directory
10 fileCount?: number
11 createdAt: string
12}
13
14interface File {
15 $type?: 'place.wisp.fs#file'
16 type: 'file'
17 blob: BlobRef
18 encoding?: 'gzip'
19 mimeType?: string
20 base64?: boolean
21}
22
23interface Directory {
24 $type?: 'place.wisp.fs#directory'
25 type: 'directory'
26 entries: Entry[]
27}
28
29interface Entry {
30 $type?: 'place.wisp.fs#entry'
31 name: string
32 node: $Typed<File> | $Typed<Directory> | $Typed<Subfs> | { $type: string }
33}
34
35interface Subfs {
36 $type?: 'place.wisp.fs#subfs'
37 type: 'subfs'
38 subject: string // AT-URI pointing to a place.wisp.subfs record
39 flat?: boolean
40}
41
42//place.wisp.subfs
43interface Main {
44 $type: 'place.wisp.subfs'
45 root: Directory
46 fileCount?: number
47 createdAt: string
48}
49
50interface File {
51 $type?: 'place.wisp.subfs#file'
52 type: 'file'
53 blob: BlobRef
54 encoding?: 'gzip'
55 mimeType?: string
56 base64?: boolean
57}
58
59interface Directory {
60 $type?: 'place.wisp.subfs#directory'
61 type: 'directory'
62 entries: Entry[]
63}
64
65interface Entry {
66 $type?: 'place.wisp.subfs#entry'
67 name: string
68 node: $Typed<File> | $Typed<Directory> | $Typed<Subfs> | { $type: string }
69}
70
71interface Subfs {
72 $type?: 'place.wisp.subfs#subfs'
73 type: 'subfs'
74 subject: string // AT-URI pointing to another place.wisp.subfs record
75}
76
77//place.wisp.settings
78interface Main {
79 $type: 'place.wisp.settings'
80 directoryListing: boolean
81 spaMode?: string
82 custom404?: string
83 indexFiles?: string[]
84 cleanUrls: boolean
85 headers?: CustomHeader[]
86}
87
88interface CustomHeader {
89 $type?: 'place.wisp.settings#customHeader'
90 name: string
91 value: string
92 path?: string // Optional glob pattern
93}
94```
95
96The main differences between place.wisp.fs and place.wisp.subfs:
97 - place.wisp.fs has a required site field
98 - place.wisp.fs#subfs has an optional flat field that place.wisp.subfs#subfs doesn't have
99
100The project is a monorepo. The package handler it uses for the typescript side is Bun. For the Rust cli, it is cargo.
101
102### Typescript Bun Workspace Layout
103
104Bun workspaces: `packages/@wisp/*`, `apps/main-app`, `apps/hosting-service`
105
106There are two typescript apps
107**`apps/main-app`** - Main backend (Bun + Elysia)
108
109- OAuth authentication and session management
110- Site CRUD operations via PDS
111- Custom domain management
112- Admin database view in /admin
113- React frontend in public/
114
115**`apps/hosting-service`** - CDN static file server (Node + Hono)
116
117- Watches AT Protocol firehose for `place.wisp.fs` record changes
118- Downloads and caches site files to disk
119- Serves sites at `https://sites.wisp.place/{did}/{site-name}` and custom domains
120- Handles redirects (`_redirects` file support) and routing logic
121- Backfill mode for syncing existing sites
122
123### Shared Packages (`packages/@wisp/*`)
124
125- **`lexicons`** - AT Protocol lexicons (`place.wisp.fs`, `place.wisp.subfs`, `place.wisp.settings`) with
126 generated TypeScript types
127- **`fs-utils`** - Filesystem tree building, manifest creation, subfs splitting logic
128- **`atproto-utils`** - AT Protocol helpers (blob upload, record operations, CID handling)
129- **`database`** - PostgreSQL schema and queries
130- **`constants`** - Shared constants (limits, file patterns, default settings)
131- **`observability`** - OpenTelemetry instrumentation
132- **`safe-fetch`** - Wrapped fetch with timeout/retry logic
133
134### CLI
135
136**`cli/`** - Rust CLI using Jacquard (AT Protocol library)
137- Direct PDS uploads without interacting with main-app
138- Can also do the same firehose watching, caching, and serving hosting-service does, just without domain management
139
140### Other Directories
141
142- **`docs/`** - Astro documentation site
143- **`binaries/`** - Compiled CLI binaries for distribution