write-nostr
v0.2.3
Published
Browser-based long-form writing app for the Nostr protocol — NIP-23 editor, feed reader, and publishing tool
Maintainers
Readme
write_nostr
write_nostr is a browser-based long-form writing app for the Nostr protocol. It provides a distraction-free Markdown editor for composing, publishing, and managing NIP-23 long-form content (kind:30023) directly to Nostr relays, along with feeds for discovering articles from the network.
Built with Svelte 5 and SvelteKit, write_nostr runs as a fully static single-page app — no backend required. Authentication works via NIP-07 browser extensions, nsec keys, or passkeys (WebAuthn/PRF). Articles are rendered with live preview, support metadata (title, summary, featured image, tags), and auto-save to localStorage drafts. The app is installable as a PWA with offline support.
Whether you're a writer looking for a censorship-resistant publishing platform or a Nostr user who wants to read and engage with long-form content on the decentralized web, write_nostr offers a clean, focused experience.
Features
- Write & Publish — Full-featured Markdown editor with live preview, formatting toolbar, metadata (title, summary, featured image, tags), and direct publishing to Nostr relays.
- Browse Articles — Three feed modes: All (global firehose), My Circle (articles from people you follow), and Top Articles (sorted by interaction score). Tag filtering, pagination, and manual refresh.
- Related Articles — Vector-similarity search across articles powered by PolyGraph, surfacing related content for any article.
- Drafts — Auto-saving drafts to localStorage with a dedicated drafts manager.
- Authentication — Sign in via NIP-07 browser extension, paste an nsec key, or use a passkey (WebAuthn/PRF).
- Social — Follow/unfollow and block/unblock authors directly from the feed.
- Profiles — Kind:0 profile metadata fetched from relays, cached for performance.
- Translation — On-device article translation via Chrome's Built-in AI Translation API.
- PWA — Installable as a progressive web app with offline support.
Tech Stack
| Layer | Technology | |---|---| | Framework | Svelte 5 + SvelteKit 2 (runes mode) | | Build | Vite 8 + TypeScript 6 | | Nostr | nostr-tools — SimplePool, event signing, NIP-19 encoding/decoding | | Passkeys | nostr-passkey — WebAuthn/PRF-based key management | | Markdown | marked — rendering | | Graph / Vector Search | PolyGraph — persistent graph with IndexedDB adapter, vector similarity search | | Caching | idb — IndexedDB cache layer | | Adapter | @sveltejs/adapter-static — static SPA output |
Nostr Protocols (NIPs)
- NIP-01 — Basic protocol & relay communication
- NIP-02 — Contact lists (kind:3)
- NIP-07 — Browser extension signer
- NIP-19 — bech32-encoded entities (
nsec,npub,naddr,nprofile,nevent) - NIP-23 — Long-form content (kind:30023)
- NIP-51 — Lists (kind:10000 mute/block lists)
- NIP-65 — Relay list metadata (kind:10002)
Getting Started
npm install
npm run dev # Development server with hot reloadOpen the URL printed by Vite (usually http://localhost:5173).
Build & Preview
npm run build # Production build to ./build
npm run preview # Preview the production build locallyDeploy
npm run deploy -- --host user@host --domain example.comThe deploy script:
- Builds the project
- Syncs to a remote server via rsync over SSH
- Installs a systemd service running the built files via the included Python SPA HTTP server
- Optionally configures Caddy or nginx as a reverse proxy with TLS
Prerequisites
- Local:
npm,rsync - Remote:
python3,systemctl,ss,curl,rsync,sudo
PolyGraph Cache
write_nostr uses PolyGraph (@0xx0lostcause0xx0/polypack), a persistent graph database built on IndexedDB, to cache Nostr events and profiles in the browser. The cache is TTL-based with automatic pruning of stale entries, and supports:
- Vector search — Articles are indexed by embedding vector, enabling similarity-based related-article recommendations and centroid-based ranking.
- Event & profile caching — Kind:30023 articles, follow lists, relay lists, and kind:0 profiles are cached locally to reduce relay round-trips.
Configuration
Environment variables: WRITE_NOSTR_SSH_TARGET, WRITE_NOSTR_DOMAIN, WRITE_NOSTR_PORT, etc.
Type Checking
npm run checkProject Structure
src/
├── app.html # HTML shell
├── app.css # Global styles (light/dark mode)
├── lib/
│ ├── components/ # Svelte components (Editor, ArticleCard, etc.)
│ ├── stores/ # Svelte stores (auth, relays, drafts, social, profiles)
│ ├── nostr/ # Nostr logic (fetch, publish, resolve, profiles)
│ └── utils/ # Utilities (markdown, NIP-19, translation)
├── routes/
│ ├── +page.svelte # Main feed page
│ ├── +layout.svelte # Root layout
│ ├── new/ # Editor (/new)
│ ├── drafts/ # Drafts manager (/drafts)
│ ├── settings/ # Settings (/settings)
│ └── article/[naddr]/ # Article view (/article/[naddr])
static/
├── brand/ # Logo assets
├── pwa/ # PWA icons
├── manifest.webmanifest # PWA manifest
└── sw.js # Service workerDefault Relays
wss://relay.damus.iowss://relay.nostr.bandwss://nos.lolwss://relay.primal.net
Relays can be managed in Settings after login.
