···11# Wisp.place
22-A static site hosting service built on the AT Protocol. [https://wisp.place](https://wisp.place)
22+33+Decentralized static site hosting on the AT Protocol. [https://wisp.place](https://wisp.place)
44+55+## What is this?
66+77+Host static sites in your AT Protocol repo, served with CDN distribution. Your PDS holds the cryptographically signed manifest and files - the source of truth. Hosting services index and serve them fast.
88+99+## Quick Start
1010+1111+```bash
1212+# Using the web interface
1313+Visit https://wisp.place and sign in
1414+1515+# Or use the CLI
1616+cd cli
1717+cargo build --release
1818+./target/release/wisp-cli your-handle.bsky.social --path ./my-site --site my-site
1919+```
2020+2121+Your site appears at `https://sites.wisp.place/{your-did}/{site-name}` or your custom domain.
32244-/src is the main backend
2323+## Architecture
52466-/hosting-service is the microservice that serves on-disk caches of sites pulled from the firehose and pdses
2525+- **`/src`** - Main backend (OAuth, site management, custom domains)
2626+- **`/hosting-service`** - Microservice that serves cached sites from disk
2727+- **`/cli`** - Rust CLI for direct PDS uploads
2828+- **`/public`** - React frontend
72988-/cli is the wisp-cli, a way to upload sites directly to the pds
3030+### How it works
3131+3232+1. Sites stored as `place.wisp.fs` records in your AT Protocol repo
3333+2. Files compressed (gzip) and base64-encoded as blobs
3434+3. Hosting service watches firehose, caches sites locally
3535+4. Sites served via custom domains or `*.wisp.place` subdomains
9361010-full readme soon
3737+## Development
3838+3939+```bash
4040+# Backend
4141+bun install
4242+bun run src/index.ts
4343+4444+# Hosting service
4545+cd hosting-service
4646+cargo run
4747+4848+# CLI
4949+cd cli
5050+cargo build
5151+```
5252+5353+## Limits
5454+5555+- Max file size: 100MB (PDS limit)
5656+- Max site size: 300MB
5757+- Max files: 2000
5858+5959+## Tech Stack
6060+6161+- Backend: Bun + Elysia + PostgreSQL
6262+- Frontend: React 19 + Tailwind 4 + Radix UI
6363+- Hosting: Rust microservice
6464+- CLI: Rust + Jacquard (AT Protocol library)
6565+- Protocol: AT Protocol OAuth + custom lexicons
6666+6767+## License
6868+6969+MIT
7070+7171+## Links
7272+7373+- [AT Protocol](https://atproto.com)
7474+- [Jacquard Library](https://tangled.org/@nonbinary.computer/jacquard)
+271
cli/README.md
···11+# Wisp CLI
22+33+A command-line tool for deploying static sites to your AT Protocol repo to be served on [wisp.place](https://wisp.place), an AT indexer to serve such sites.
44+55+## Why?
66+77+The PDS serves as a way to verfiably, cryptographically prove that you own your site. That it was you (or at least someone who controls your account) who uploaded it. It is also a manifest of each file in the site to ensure file integrity. Keeping hosting seperate ensures that you could move your site across other servers or even serverless solutions to ensure speedy delievery while keeping it backed by an absolute source of truth being the manifest record and the blobs of each file in your repo.
88+99+## Features
1010+1111+- Deploy static sites directly to your AT Protocol repo
1212+- Supports both OAuth and app password authentication
1313+- Preserves directory structure and file integrity
1414+1515+## Soon
1616+1717+-- Host sites
1818+-- Manage and delete sites
1919+-- Metrics and logs for self hosting.
2020+2121+## Installation
2222+2323+### From Source
2424+2525+```bash
2626+cargo build --release
2727+```
2828+2929+Check out the build scripts for cross complation using nix-shell.
3030+3131+The binary will be available at `target/release/wisp-cli`.
3232+3333+## Usage
3434+3535+### Basic Deployment
3636+3737+Deploy the current directory:
3838+3939+```bash
4040+wisp-cli nekomimi.ppet --path . --site my-site
4141+```
4242+4343+Deploy a specific directory:
4444+4545+```bash
4646+wisp-cli alice.bsky.social --path ./dist/ --site my-site
4747+```
4848+4949+### Authentication Methods
5050+5151+#### OAuth (Recommended)
5252+5353+By default, the CLI uses OAuth authentication with a local loopback server:
5454+5555+```bash
5656+wisp-cli alice.bsky.social --path ./my-site --site my-site
5757+```
5858+5959+This will:
6060+1. Open your browser for authentication
6161+2. Save the session to a file (default: `/tmp/wisp-oauth-session.json`)
6262+3. Reuse the session for future deployments
6363+6464+Specify a custom session file location:
6565+6666+```bash
6767+wisp-cli alice.bsky.social --path ./my-site --site my-site --store ~/.wisp-session.json
6868+```
6969+7070+#### App Password
7171+7272+For headless environments or CI/CD, use an app password:
7373+7474+```bash
7575+wisp-cli alice.bsky.social --path ./my-site --site my-site --password YOUR_APP_PASSWORD
7676+```
7777+7878+**Note:** When using `--password`, the `--store` option is ignored.
7979+8080+## Command-Line Options
8181+8282+```
8383+wisp-cli [OPTIONS] <INPUT>
8484+8585+Arguments:
8686+ <INPUT> Handle (e.g., alice.bsky.social), DID, or PDS URL
8787+8888+Options:
8989+ -p, --path <PATH> Path to the directory containing your static site [default: .]
9090+ -s, --site <SITE> Site name (defaults to directory name)
9191+ --store <STORE> Path to auth store file (only used with OAuth) [default: /tmp/wisp-oauth-session.json]
9292+ --password <PASSWORD> App Password for authentication (alternative to OAuth)
9393+ -h, --help Print help
9494+ -V, --version Print version
9595+```
9696+9797+## How It Works
9898+9999+1. **Authentication**: Authenticates using OAuth or app password
100100+2. **File Processing**:
101101+ - Recursively walks the directory tree
102102+ - Skips hidden files (starting with `.`)
103103+ - Detects MIME types automatically
104104+ - Compresses files with gzip
105105+ - Base64 encodes compressed content
106106+3. **Upload**:
107107+ - Uploads files as blobs to your PDS
108108+ - Processes up to 5 files concurrently
109109+ - Creates a `place.wisp.fs` record with the site manifest
110110+4. **Deployment**: Site is immediately available at `https://sites.wisp.place/{did}/{site-name}`
111111+112112+## File Processing
113113+114114+All files are automatically:
115115+116116+- **Compressed** with gzip (level 9)
117117+- **Base64 encoded** to bypass PDS content sniffing
118118+- **Uploaded** as `application/octet-stream` blobs
119119+- **Stored** with original MIME type metadata
120120+121121+The hosting service automatically decompresses non HTML/CSS/JS files when serving them.
122122+123123+## Limitations
124124+125125+- **Max file size**: 100MB per file (after compression) (this is a PDS limit, but not enforced by the CLI in case yours is higher)
126126+- **Max file count**: 2000 files
127127+- **Site name** must follow AT Protocol rkey format rules (alphanumeric, hyphens, underscores)
128128+129129+## Deploy with CI/CD
130130+131131+### GitHub Actions
132132+133133+```yaml
134134+name: Deploy to Wisp
135135+on:
136136+ push:
137137+ branches: [main]
138138+139139+jobs:
140140+ deploy:
141141+ runs-on: ubuntu-latest
142142+ steps:
143143+ - uses: actions/checkout@v3
144144+145145+ - name: Setup Node
146146+ uses: actions/setup-node@v3
147147+ with:
148148+ node-version: '25'
149149+150150+ - name: Install dependencies
151151+ run: npm install
152152+153153+ - name: Build site
154154+ run: npm run build
155155+156156+ - name: Download Wisp CLI
157157+ run: |
158158+ curl -L https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-x86_64-linux -o wisp-cli
159159+ chmod +x wisp-cli
160160+161161+ - name: Deploy to Wisp
162162+ env:
163163+ WISP_APP_PASSWORD: ${{ secrets.WISP_APP_PASSWORD }}
164164+ run: |
165165+ ./wisp-cli alice.bsky.social \
166166+ --path ./dist \
167167+ --site my-site \
168168+ --password "$WISP_APP_PASSWORD"
169169+```
170170+171171+### Tangled.org
172172+173173+```yaml
174174+when:
175175+ - event: ['push']
176176+ branch: ['main']
177177+ - event: ['manual']
178178+179179+engine: 'nixery'
180180+181181+clone:
182182+ skip: false
183183+ depth: 1
184184+ submodules: false
185185+186186+dependencies:
187187+ nixpkgs:
188188+ - nodejs
189189+ - coreutils
190190+ - curl
191191+ github:NixOS/nixpkgs/nixpkgs-unstable:
192192+ - bun
193193+194194+environment:
195195+ SITE_PATH: 'dist'
196196+ SITE_NAME: 'my-site'
197197+ WISP_HANDLE: 'your-handle.bsky.social'
198198+199199+steps:
200200+ - name: build site
201201+ command: |
202202+ export PATH="$HOME/.nix-profile/bin:$PATH"
203203+204204+ # regenerate lockfile
205205+ rm package-lock.json bun.lock
206206+ bun install @rolldown/binding-linux-arm64-gnu --save-optional
207207+ bun install
208208+209209+ # build with vite
210210+ bun node_modules/.bin/vite build
211211+212212+ - name: deploy to wisp
213213+ command: |
214214+ # Download Wisp CLI
215215+ curl https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-x86_64-linux -o wisp-cli
216216+ chmod +x wisp-cli
217217+218218+ # Deploy to Wisp
219219+ ./wisp-cli \
220220+ "$WISP_HANDLE" \
221221+ --path "$SITE_PATH" \
222222+ --site "$SITE_NAME" \
223223+ --password "$WISP_APP_PASSWORD"
224224+```
225225+226226+### Generic Shell Script
227227+228228+```bash
229229+# Use app password from environment variable
230230+wisp-cli alice.bsky.social --path ./dist --site my-site --password "$WISP_APP_PASSWORD"
231231+```
232232+233233+## Output
234234+235235+Upon successful deployment, you'll see:
236236+237237+```
238238+Deployed site 'my-site': at://did:plc:abc123xyz/place.wisp.fs/my-site
239239+Available at: https://sites.wisp.place/did:plc:abc123xyz/my-site
240240+```
241241+242242+### Dependencies
243243+244244+- **jacquard**: AT Protocol client library
245245+- **clap**: Command-line argument parsing
246246+- **tokio**: Async runtime
247247+- **flate2**: Gzip compression
248248+- **base64**: Base64 encoding
249249+- **walkdir**: Directory traversal
250250+- **mime_guess**: MIME type detection
251251+252252+## License
253253+254254+MIT License
255255+256256+## Contributing
257257+258258+Just don't give me entirely claude slop especailly not in the PR description itself. You should be responsible for code you submit and aware of what it even is you're submitting.
259259+260260+## Links
261261+262262+- **Website**: https://wisp.place
263263+- **Main Repository**: https://tangled.org/@nekomimi.pet/wisp.place-monorepo
264264+- **AT Protocol**: https://atproto.com
265265+- **Jacquard Library**: https://tangled.org/@nonbinary.computer/jacquard
266266+267267+## Support
268268+269269+For issues and questions:
270270+- Check the main wisp.place documentation
271271+- Open an issue in the main repository