write-nostr
v0.4.11
Published
Browser-based long-form writing app for the Nostr protocol — NIP-23 editor, feed reader, and publishing tool
Downloads
1,289
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.
- Semantic Embeddings — Starts with an offline deterministic baseline, then optionally upgrades to local Transformers.js embeddings in a Web Worker using WebGPU/WASM with automatic fallback.
- 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.
- AI Writing Assistant — Optional Habla-inspired side-panel tools for drafting, rewriting, concision, expansion, and summaries. Configure an OpenAI or Groq API key locally in Settings.
- 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 OPFS adapter, vector similarity search, and adaptive memory | | Local Embeddings | Transformers.js — optional browser sentence embeddings with FeatureHashEmbedding fallback | | 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 locallyAndroid build
Android APKs and app bundles are built by GitHub Actions when a v* release tag
is pushed. The workflow installs the Android SDK and NDK on the GitHub-hosted
runner, initializes Tauri's generated Android project there, signs the ARM64 APK
and AAB with the Android release key, and uploads both to the workflow run and
GitHub release. No Android development tools are needed on your local machine.
Configure these repository secrets for Android release signing:
ANDROID_KEYSTORE_BASE64— base64-encoded PKCS#12 keystoreANDROID_KEYSTORE_PASSWORD- The signing key alias is read from the keystore during the build.
Tagged Android releases are also published to Zapstore using zapstore.yaml.
Configure the repository secret ZAPSTORE_NSEC with the Zapstore publisher
Nostr private key used by Scrollstr. The workflow validates the metadata, then
publishes the signed APK after uploading it to the GitHub release.
Deploy
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.
- Embedding upgrade path — FeatureHashEmbedding is available immediately and offline. On capable browsers, a local
all-MiniLM-L6-v2Transformers.js model is loaded in a Worker, preferring WebGPU and falling back to WASM. Existing persisted vectors are rebuilt in background batches and tagged with provider/version and input-hash metadata. - Event & profile caching — Kind:30023 articles, follow lists, relay lists, and kind:0 profiles are cached locally to reduce relay round-trips.
- Adaptive memory (activation) — Reading, liking, boosting, zapping, and searching durably reinforce article nodes; edges link articles to their topics (
tagged), author (authors), and mentioned pubkeys (mentions). A personalized For You feed ranks by activation, semantic search "absorbs" the query region, and similar articles are re-ranked by spreading activation.
Configuration
Environment variables: WRITE_NOSTR_SSH_TARGET, WRITE_NOSTR_DOMAIN, WRITE_NOSTR_PORT, etc.
AI Writing Assistant
The editor includes an optional AI assistant modeled after the writing workflows in Habla. It supports OpenAI and Groq OpenAI-compatible chat endpoints. API keys are stored locally in the browser, scoped to the logged-in Nostr account, and are sent directly to the selected provider; they are never published to Nostr. Generated text is shown for review and is never applied or published automatically.
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.
