OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note commits / 41662a9

Update docs for content-addressed media, HLS video, and data export
Add documentation for the media storage refactor (SHA-256 dedup, streaming range requests), HLS adaptive video pipeline, data export endpoints, migration CLI commands, and ffmpeg system dependency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
41662a9529f9e774f4637767cb45df2ef529dc3a
by plankatron <fleeky@gmail.com> · 2026-03-19T18:33:22.000Z

Changed files (2) +476 -30

README.md +393 -25
……@@ -1,58 +1,350 @@
11# Hypernote
22
3P2P note-taking server with per-notebook Hyperdrives, end-to-end encryption, wiki-links, and multi-writer collaboration.
3P2P note-taking server with per-notebook Hyperdrives, end-to-end encryption, wiki-links, and a built-in WYSIWYG editor.
44
5Built on [Holepunch](https://holepunch.to): Hypercore, Hyperdrive, Hyperbee, Hyperswarm, Autobase.
5Built on the [Holepunch](https://holepunch.to) stack: Hypercore, Hyperdrive, Hyperbee, and Hyperswarm.
66
77## Features
88
9- Per-notebook Hyperdrives with selective P2P replication
10- Multi-writer collaboration via Autobase + Yjs CRDTs
11- End-to-end encryption (Argon2id + XSalsa20-Poly1305)
12- Ed25519 browser identity with challenge-response auth
13- Wiki-links, backlinks, and knowledge graph
14- WYSIWYG editor (Toast UI), media upload, gallery
15- Hyper:// site publishing for Agregore browser
16- Zero CDN dependencies (works offline)
9- **Per-notebook Hyperdrives** -- each notebook is an isolated Hyperdrive that can be selectively shared and replicated
10- **P2P replication** -- announce notebooks on Hyperswarm for real-time peer-to-peer sync
11- **Hyper:// site publishing** -- auto-publishes a read/write site drive accessible via [Agregore](https://agregore.mauve.moe/) browser
12- **End-to-end encryption** -- per-notebook encryption with Argon2id key derivation and XSalsa20-Poly1305
13- **Ed25519 identity** -- passwordless auth via auto-generated browser keypairs with challenge-response signing
14- **Wiki-links & backlinks** -- `[[Note Title]]` syntax with automatic backlink indexing and knowledge graph
15- **WYSIWYG editor** -- Toast UI Editor with markdown, image upload, and video support
16- **Content-addressed media** -- SHA-256 dedup via Hyperblobs, streaming serve with HTTP range requests (206 Partial Content)
17- **HLS adaptive video** -- upload video and it auto-transcodes to 480p/720p/1080p HLS segments (requires ffmpeg)
18- **Data export** -- export individual notebooks or the entire server as JSON
19- **Public sharing** -- `/:username/:notebook/:noteId` URLs for shared notebooks
20- **Note merge & split** -- combine notes or split at heading boundaries
21- **Full-text search** -- search across all notebooks with context snippets
22- **Zero CDN dependencies** -- all assets served locally (works offline and in hyper://)
23- **CLI + Web UI + REST API** -- three ways to interact
1724
18## Quick Start
25## Install
26
27### From pear-git (P2P)
1928
2029```bash
30# 1. Install pear-git (if you don't have pgh)
31npm install -g pear
32pear dump pear://dpjhhzqgm6wqxc9nmw1y3m7eaptfufr6o5p7r4xegjghju8mkweo ~/.local/share/pear-git
33cat > ~/.local/bin/pgh << 'EOF'
34#!/bin/sh
35exec bare ~/.local/share/pear-git/index.js pgh "$@"
36EOF
37chmod +x ~/.local/bin/pgh
38
39# 2. Clone hypernote
40pgh repo clone punch://1290678432f9573b463d4472a9426c5408b34579bf8576e26957a1321fd5e836/hyper-note
41
42# 3. Install and run
43cd hyper-note
2144npm install
2245bare hypernote-server.js serve
46```
2347
24# LAN accessible
25bare hypernote-server.js serve 4040 --network
48### System Dependencies
2649
27# Behind reverse proxy
28bare hypernote-server.js serve 4040 --network --prefix=/hypernote
29```
50- **[Bare runtime](https://github.com/nicknisi/bare)** -- JavaScript runtime for P2P applications
51- **ffmpeg** -- required for HLS video transcoding (`apt install ffmpeg` / `brew install ffmpeg`)
3052
31On first start, an admin token is printed to the console. Open `http://localhost:4040` -- an ed25519 identity is generated automatically and the first user is promoted to admin.
53### From local checkout
3254
33## Install via pear-git
55```bash
56git clone <repo-url> hyper-note
57cd hyper-note
58npm install
59bare hypernote-server.js serve
60```
61
62### As a Pear app (auto-updates)
3463
3564```bash
3665pgh app install pear://p84snzmpxca3ii7nfdqjbd5wxjdqzccpoj9xjwzz96wpe4m557zo --name hypernote
3766hypernote serve
3867```
3968
69## Quick Start
70
71```bash
72# Start server (localhost:4040)
73bare hypernote-server.js serve
74
75# Start on LAN (accessible from other devices)
76bare hypernote-server.js serve 4040 --network
77
78# Custom port, storage, and URL prefix
79bare hypernote-server.js serve 5555 --storage=/path/to/data --prefix=/hypernote
80
81# Promote a user to admin (CLI)
82bare hypernote-server.js promote <publicKey|username>
83```
84
85On first start, the server prints an admin bearer token to the console. Open `http://localhost:4040` in your browser -- an ed25519 identity is generated automatically and the first user is promoted to admin.
86
87### Reverse Proxy
88
89Use `--prefix` when running behind a reverse proxy:
90
91```bash
92bare hypernote-server.js serve 4040 --network --prefix=/hypernote
93```
94
95Access at `http://yourhost/hypernote/`. The prefix is stripped from API routes automatically.
96
97### Agregore (hyper://)
98
99The server auto-publishes a site drive on Hyperswarm. Access via Agregore browser:
100
101```
102hyper://SITE_KEY/
103```
104
105The site key is shown in the server logs and available at `GET /api/site`. When accessed via `hyper://`, the app connects back to the local HTTP server for full read/write access (not read-only).
106
40107## CLI
41108
42109```bash
110# Notebooks
43111hn notebook list
44112hn notebook create "Work Notes"
113hn notebook delete work-notes
114hn notebook rename work-notes new-slug
115
116# Notes
117hn note list work-notes
45118hn note create work-notes --title "Meeting" --content "# Agenda\n..."
46hn search "query"
119hn note get work-notes <noteId>
120hn note update work-notes <noteId> --title "Updated"
121hn note delete work-notes <noteId>
122hn note move work-notes <noteId> --to personal
123
124# Search
125hn search "query" --notebook work-notes
126
127# Encryption
128hn share encrypt work-notes --password secret
129hn share decrypt work-notes --password secret
130hn share unlock work-notes --password secret
131
132# Links
133hn links work-notes <noteId>
134
135# P2P Sync
47136hn sync status
137hn sync announce work-notes
138hn sync add <driveKey> --slug remote-notes
139hn sync remove remote-notes
140
141# Migration (Hyperdrive media → content-addressed MediaStore)
142bare hypernote-server.js migrate verify [--storage=path]
143bare hypernote-server.js migrate cleanup [--storage=path]
144```
145
146Configure the CLI:
147
148```bash
149# Stored in ~/.config/hypernote/cli.json
150hn config set server http://localhost:4040
151hn config set token <bearer-token>
152```
153
154## Architecture
155
156```
157Browser (SPA) Server (Bare runtime)
158 identity.js AuthManager
159 ed25519 keypair WebIdentityProvider
160 challenge signing BearerTokenProvider
161 fetch interception KeetIdentityProvider
162 api.js NoteManager
163 HTTP client per-notebook CRUD
164 hyper:// dual-mode DriveManager
165 app.js Hyperdrive lifecycle
166 hash router ReplicationManager
167 Toast UI Editor Hyperswarm P2P sync
168 IdentityRegistry
169 user accounts + permissions
170 LinkManager
171 wiki-links + backlinks
172 NotebookCrypto
173 per-notebook encryption
174 MediaStore
175 SHA-256 content-addressed blobs
176 VideoProcessor
177 HLS transcoding (ffmpeg)
178 SitePublisher
179 hyper:// site drive
48180```
49181
50Configure: `hn config set server http://localhost:4040`
182### Storage Layout
183
184```
185~/.config/hypernote/
186 storage-locator.json # breadcrumb to find storage
187 server-data/
188 server-identity.json # device ed25519 keypair
189 device-mnemonic.enc # encrypted BIP39 mnemonic vault
190 corestore/
191 registry/ # Hyperbee (users, tokens, metadata)
192 notebook-general/ # Hyperdrive for "general" notebook
193 notebook-work/ # Hyperdrive for "work" notebook
194 site-public/ # Hyperdrive for hyper:// site
195 ...
196```
197
198Media is stored in a shared, content-addressed Hyperblobs core (MediaStore). Duplicate files are automatically deduplicated by SHA-256 hash. Legacy per-notebook Hyperdrive media paths are supported via backward-compatible filename-to-hash mappings.
199
200Each notebook Hyperdrive contains:
201
202```
203/metadata.json # notebook name, description, flags
204/notes/{noteId}.json # individual notes (title, content, tags)
205```
206
207### Authentication
208
209Three auth methods, tried in order:
210
2111. **Web Identity** -- browser generates ed25519 keypair **client-side** using @noble/ed25519 (loaded locally, no CDN). Signs server challenges. Compatible with wirecore/wiredove ANProto format. Headers: `X-Wirecore-PublicKey`, `X-Wirecore-Signature`, `X-Wirecore-Challenge`
2122. **Bearer Token** -- `Authorization: Bearer <token>` for CLI and API clients
2133. **Keet Identity** -- BIP39 mnemonic-based, for cross-device portability
214
215### P2P Replication
216
217Each notebook is a Hyperdrive. To share:
218
2191. **Announce** a notebook on Hyperswarm (`POST /api/notebooks/:slug/announce`)
2202. Peers discover it via the discovery key
2213. Remote peers add it by drive key (`POST /api/sync/add`)
2224. Hyperdrive handles incremental replication automatically
223
224Only announced notebooks are discoverable. The registry (users, tokens, permissions) is never replicated.
225
226### Multi-Writer / Device Linking
227
228Hypernote supports linking multiple instances via [Autobase](https://github.com/holepunchto/autobase) for multi-device and multi-user collaboration.
229
230**How it works:**
2311. An admin on instance A creates a link invite (`POST /api/multiwriter/link`) and gets an invite hex
2322. An admin on instance B joins with that invite (`POST /api/multiwriter/join`)
2333. Both instances share a replicated registry (users, permissions) via Autobase
2344. Mirror sync copies notebooks between instances in the background
235
236**Key endpoints:**
237- `GET /api/multiwriter/status` -- check linking state and peers
238- `POST /api/multiwriter/link` / `POST /api/multiwriter/join` -- instance linking
239- `POST /api/multiwriter/sync` -- trigger manual mirror sync
240- `POST /api/multiwriter/mirror` -- start/stop background mirror sync
241- `POST /api/notebooks/:nb/share` -- toggle Autobase sharing on a notebook
242- Shared notebooks get their own CRDT-based note endpoints (`/shared-notes`, Yjs updates)
243
244See [docs/api.md](docs/api.md) for the complete multiwriter API reference.
245
246### Site Publisher
247
248The server automatically publishes a Hyperdrive containing:
249- All web UI files (`/index.html`, `/web/*`)
250- Notebook data as static JSON (`/api/notebooks.json`, `/api/notebooks/:slug/*`)
251- Media files (`/api/media/:slug/*`)
252- Recent notes feed (`/api/recent.json`)
253
254The site drive is announced on Hyperswarm and accessible via `hyper://SITE_KEY/` in Agregore. When the app detects hyper:// protocol, it routes API calls to the configured local HTTP server for full read/write access.
255
256## API
257
258See [docs/api.md](docs/api.md) for the complete REST API reference.
259
260### Quick Reference
261
262| Method | Endpoint | Description |
263|--------|----------|-------------|
264| GET | `/api/health` | Server health check |
265| GET | `/api/status` | Server status + identity + site key |
266| GET | `/api/whoami` | Current user info |
267| GET | `/api/site` | Site drive key + hyper:// URL |
268| POST | `/api/site/publish` | Force republish site (admin) |
269| GET | `/api/recent` | Recent notes feed |
270| GET | `/api/notebooks` | List notebooks (?mine=true, ?owner=) |
271| POST | `/api/notebooks` | Create notebook |
272| GET | `/api/notebooks/:nb` | Get notebook metadata |
273| PATCH | `/api/notebooks/:nb` | Update notebook |
274| DELETE | `/api/notebooks/:nb` | Delete notebook |
275| POST | `/api/notebooks/:nb/rename` | Rename notebook slug |
276| GET | `/api/notebooks/:nb/notes` | List notes (?limit, ?offset) |
277| POST | `/api/notebooks/:nb/notes` | Create note |
278| GET | `/api/notebooks/:nb/notes/:id` | Get note |
279| PATCH | `/api/notebooks/:nb/notes/:id` | Update note |
280| DELETE | `/api/notebooks/:nb/notes/:id` | Delete note |
281| POST | `/api/notebooks/:nb/notes/:id/move` | Move note to another notebook |
282| POST | `/api/notebooks/:nb/media` | Upload media (content-addressed) |
283| GET | `/api/notebooks/:nb/media/:identifier` | Serve media (streaming, range requests) |
284| GET | `/api/notebooks/:nb/media/:hash/stream` | HLS master playlist |
285| GET | `/api/notebooks/:nb/media/:hash/stream/:seg` | HLS segment |
286| GET | `/api/media/:hash/status` | Video processing status |
287| GET | `/api/export/:notebook` | Export notebook (read auth) |
288| GET | `/api/export` | Export all notebooks (admin) |
289| GET | `/api/search?q=term` | Full-text search |
290| POST | `/api/notebooks/:nb/encrypt` | Encrypt notebook |
291| POST | `/api/notebooks/:nb/decrypt` | Decrypt notebook |
292| POST | `/api/notebooks/:nb/unlock` | Unlock encrypted notebook |
293| POST | `/api/notebooks/:nb/share` | Share notebook (token or multiwriter) |
294| GET | `/api/notebooks/:nb/shares` | List share tokens (admin) |
295| DELETE | `/api/notebooks/:nb/shares/:hash` | Revoke share token (admin) |
296| GET | `/api/notebooks/:nb/notes/:id/links` | Get outgoing wiki-links |
297| GET | `/api/notebooks/:nb/notes/:id/backlinks` | Get backlinks |
298| GET | `/api/graph` | Knowledge graph (?notebook=) |
299| POST | `/api/notebooks/:nb/merge` | Merge two notes |
300| POST | `/api/notebooks/:nb/notes/:id/split` | Split note at headings |
301| POST | `/api/notebooks/:nb/announce` | Announce on Hyperswarm |
302| POST | `/api/notebooks/:nb/unannounce` | Stop announcing |
303| GET | `/api/notebooks/:nb/drive-info` | Hyperdrive info |
304| GET | `/api/sync/status` | P2P sync status |
305| POST | `/api/sync/add` | Add remote notebook |
306| DELETE | `/api/sync/remote/:nb` | Remove remote notebook |
307| POST | `/api/auth/challenge` | Get signing challenge |
308| GET | `/api/user/profile` | Get user profile |
309| PATCH | `/api/user/profile` | Update display name/theme |
310| POST | `/api/user/username` | Set username |
311| GET | `/api/user/username/:u/available` | Check username availability |
312| GET | `/api/users` | Public user directory |
313| GET | `/api/admin/users` | List all users (admin) |
314| DELETE | `/api/admin/users/:id` | Delete user (admin) |
315| GET | `/api/admin/stats` | Server stats (admin) |
316| POST | `/api/admin/promote` | Promote user (admin) |
317| POST | `/api/admin/demote` | Demote user (admin) |
318| POST | `/api/admin/notebooks/:nb/transfer` | Transfer ownership (admin) |
319| GET | `/api/multiwriter/status` | Multi-writer status (admin) |
320| POST | `/api/multiwriter/link` | Create link invite (admin) |
321| POST | `/api/multiwriter/join` | Join with invite (admin) |
322| POST | `/api/multiwriter/add-writer` | Add writer key (admin) |
323| POST | `/api/multiwriter/sync` | Trigger mirror sync (admin) |
324| POST | `/api/multiwriter/mirror` | Start/stop mirror (admin) |
325| GET | `/api/notebooks/:nb/shared-notes` | List shared notes |
326| POST | `/api/notebooks/:nb/shared-notes` | Create shared note |
327| POST | `/api/notebooks/:nb/shared-notes/:id/yjs` | Apply Yjs update |
328| GET | `/api/notebooks/:nb/shared-notes/:id/content` | Get shared note content |
329| GET | `/api/debug/identity-chain` | Identity chain (localhost) |
51330
52331## Deployment
53332
333### pear-git CI/CD
334
335Hypernote deploys automatically via pear-git:
336
54337```bash
55git push pear master # triggers auto-deploy via pear-git CI/CD
338git push pear master # triggers auto-deploy
339pgh deploy log hyper-note # check deploy status
340```
341
342The CI pipeline (`.pear-git/ci.yaml`) installs dependencies, then pear-git runs `pear stage` -> `pear release` -> `pear seed`.
343
344### Manual
345
346```bash
347bare hypernote-server.js serve 4040 --network
56348```
57349
58350For production, run in tmux or as a systemd service.
……@@ -60,13 +352,89 @@ For production, run in tmux or as a systemd service.
60352## Testing
61353
62354```bash
63npm test # 150+ assertions across 17 test files
355# Run all tests (150+ assertions)
356npm test
357
358# Run specific test
359bare node_modules/.bin/brittle test/unit/note-manager.test.js
64360```
65361
66## Documentation
362Test coverage: identity, auth, notebooks, notes, encryption, sharing, links, merge/split, replication, rate limiting, storage, CLI config, web routes.
363
364## Project Structure
67365
68- [REST API Reference](docs/api.md) -- all 65+ endpoints
69- [Architecture](docs/architecture.md) -- system design, storage layout, auth, security model, project structure
366```
367hypernote/
368 hypernote-server.js # server entry point
369 package.json
370 bin/hn # CLI entry point
371 .pear-git/ci.yaml # pear-git CI config
372 lib/
373 auth.js # AuthManager (multi-method)
374 note-manager.js # notebook + note CRUD
375 drive-manager.js # per-notebook Hyperdrive lifecycle
376 replication.js # Hyperswarm P2P sync
377 site-publisher.js # hyper:// site drive publisher
378 notebook-crypto.js # per-notebook encryption
379 link-manager.js # wiki-links + backlinks + graph
380 media-store.js # SHA-256 content-addressed blob storage
381 export.js # data export (notebook + full server)
382 anproto.js # ANProto ed25519 signatures
383 storage-locator.js # storage breadcrumb system
384 errors.js # custom error classes
385 response.js # HTTP response helpers
386 media/
387 video-processor.js # HLS transcoding via ffmpeg
388 multiwriter/
389 manager.js # multi-writer orchestration
390 instance-linker.js # invite-based instance linking
391 shared-registry.js # Autobase-backed shared user registry
392 registry-apply.js # registry operation application
393 auth/
394 base-provider.js # auth provider interface
395 bearer-token.js # bearer token auth
396 web-identity.js # browser ed25519 auth
397 keet-identity.js # BIP39 mnemonic auth
398 challenge-manager.js # challenge-response protocol
399 identity-registry.js # user + identity storage
400 rate-limiter.js # per-IP rate limiting
401 identity/
402 identity-manager.js # device keypair management
403 secure-storage.js # encrypted mnemonic vault
404 routes/
405 index.js # HTTP router
406 auth.js # identity + auth endpoints
407 notebooks.js # notebook + note + media endpoints
408 sharing.js # encryption + sharing endpoints
409 links.js # links + backlinks + graph endpoints
410 sync.js # P2P sync endpoints
411 multiwriter.js # instance linking + shared notebooks
412 web.js # static files + vendor + public URLs
413 cli/
414 client.js # HTTP client for CLI
415 config.js # CLI config file
416 output.js # CLI output formatting
417 commands/ # CLI subcommands
418 web/
419 index.html # SPA entry point
420 js/
421 app.js # main app + router + views
422 api.js # HTTP client (dual-mode: HTTP + hyper://)
423 identity.js # ed25519 auth + fetch interception
424 markdown.js # markdown rendering
425 css/
426 app.css # dark theme styles
427 vendor/
428 toastui-editor-all.min.js # Toast UI editor (local, no CDN)
429 toastui-editor.min.css # Toast UI styles (local, no CDN)
430 hls.min.js # HLS.js video player (local, no CDN)
431 test/
432 unit/ # 17 test files (brittle)
433 helpers/
434 fixtures.js # test helpers
435 docs/
436 api.md # REST API reference
437```
70438
71439## License
72440
docs/api.md +83 -5
……@@ -313,11 +313,13 @@ Move a note to a different notebook. Requires `write` permission.
313313
314314## Media
315315
316All media is stored in a shared, content-addressed Hyperblobs core (MediaStore). Files are deduplicated by SHA-256 hash. Uploaded videos are automatically transcoded to HLS adaptive streams (480p/720p/1080p) if ffmpeg is installed.
317
316318### POST /api/notebooks/:notebook/media
317319
318320Upload a media file (image, video, audio). Requires `write` permission.
319321
320Send the raw binary body with the appropriate `Content-Type` header.
322Send the raw binary body with the appropriate `Content-Type` header. The server computes a SHA-256 hash and deduplicates against existing blobs. Videos are automatically enqueued for HLS transcoding in the background.
321323
322324```bash
323325curl -X POST \
……@@ -329,18 +331,94 @@ curl -X POST \
329331**Response (201):**
330332```json
331333{
332 "url": "/api/notebooks/work/media/1708000000000-a1b2c3d4.png",
333 "filename": "1708000000000-a1b2c3d4.png",
334 "url": "/api/notebooks/work/media/a1b2c3d4e5f6...",
335 "hash": "a1b2c3d4e5f6...",
336 "filename": "a1b2c3d4e5f6...",
334337 "size": 45032,
335338 "contentType": "image/png"
336339}
337340```
338341
339### GET /api/notebooks/:notebook/media/:filename
342The `hash` is a hex-encoded SHA-256 of the file content. Uploading the same content returns the same hash (dedup).
343
344### GET /api/notebooks/:notebook/media/:identifier
340345
341346Serve a media file. **No auth required** -- media URLs are public.
342347
343Returns the binary file with correct `Content-Type` and `Cache-Control: public, max-age=31536000, immutable`.
348Supports HTTP range requests for streaming large files (video, audio). The server responds with `206 Partial Content` when a `Range` header is present.
349
350Returns the binary file with correct `Content-Type`, `Accept-Ranges: bytes`, and `Cache-Control: public, max-age=31536000, immutable`.
351
352The `:identifier` can be a SHA-256 hash (content-addressed) or a legacy filename (backward-compatible via migration mappings).
353
354### GET /api/notebooks/:notebook/media/:hash/stream
355
356Get the HLS master playlist for a transcoded video. Returns an M3U8 playlist with links to quality levels (480p, 720p, 1080p).
357
358Returns `404` if the video is still processing or has no HLS variants.
359
360**Response headers:**
361- `Content-Type: application/vnd.apple.mpegurl`
362
363### GET /api/notebooks/:notebook/media/:hash/stream/:segmentHash
364
365Serve an HLS segment or level playlist. Level playlists (`.m3u8`) have their segment filenames rewritten to content-addressed URLs. Transport stream segments (`.ts`) are served with immutable caching.
366
367### GET /api/media/:hash/status
368
369Check the HLS transcoding status of an uploaded video. **No auth required.**
370
371```json
372{
373 "status": "ready",
374 "hash": "a1b2c3d4e5f6...",
375 "manifest": "deadbeef...",
376 "levels": [
377 { "quality": "480p", "m3u8": "...", "segments": [...] },
378 { "quality": "720p", "m3u8": "...", "segments": [...] },
379 { "quality": "1080p", "m3u8": "...", "segments": [...] }
380 ]
381}
382```
383
384Possible `status` values: `queued`, `processing`, `ready`, `failed`, `unknown`.
385
386---
387
388## Data Export
389
390### GET /api/export/:notebook
391
392Export a single notebook as JSON (notes + metadata). Requires `read` permission on the notebook.
393
394```json
395{
396 "format": "hypernote-export-v1",
397 "exportedAt": "2026-03-19T12:00:00.000Z",
398 "notebook": {
399 "slug": "work-notes",
400 "name": "Work Notes",
401 "notes": [
402 { "id": "abc123", "title": "Meeting Notes", "content": "...", "tags": [...] }
403 ]
404 }
405}
406```
407
408Returns `404` if the notebook does not exist.
409
410### GET /api/export
411
412Export all notebooks on the server. Requires `admin` permission.
413
414```json
415{
416 "format": "hypernote-export-v1",
417 "exportedAt": "2026-03-19T12:00:00.000Z",
418 "version": "0.2.0",
419 "notebooks": [...]
420}
421```
344422
345423---
346424