A Nostr web application for community leaders to create and share relay recommendations. Members review the canonical template, explicitly connect a signer, then separately approve publication of relay lists (kind 10002), Blossom servers (kind 10063), and DM relays (kind 10050).
When new users join Nostr, one of the biggest friction points is choosing and configuring relays. Communitator solves this by letting community leaders create shareable relay templates that users can apply instantly.
- Create Relay Templates: Community leaders can define relay sets with read/write permissions
- Shareable Links: Generate unique URLs for each template
- Explicit review and consent: Connect and Apply are separate; each present event kind is reviewed before signing
- Blossom Server Support: Configure blossom servers (kind 10063)
- DM Relay Support: Set up private messaging relays (kind 10050)
- Community Templates: Pre-configured templates for popular communities
- Blast Relays: Signed events are published to a configured blast relay set and only positive NIP-01
OKacknowledgements count as accepted - Browser Extension Support: Works with Alby, Nos2x, and other NIP-07 extensions
- Create a template with:
- Main relays (with read/write permissions)
- Blossom servers (for media hosting)
- DM relays (for private messages)
- Template name and description
- Generate a shareable link
- Share the link with your community
- Click the shared template link
- Connect your Nostr extension
- Review normalized endpoints, permissions, event kinds, every configured blast destination, and every template relay destination
- Choose Apply and approve each signer prompt
- Inspect complete, partial, failed, or cancelled destination results; retry only failures when offered
- React with Vite for fast development
- nostr-tools for Nostr protocol operations
- NIP-07 extension support (Alby, Nos2x, etc.)
- Pure CSS with responsive design
- Node.js 24.19.0 (the supported version is recorded in
.nvmrc) - npm 11
- A Nostr extension (Alby, Nos2x, etc.)
Clone the repository.
# Install the exact locked dependency graph
npm ci
# Start development server
npm run dev
# Build for production
npm run build
The development server starts at http://localhost:3000. If a browser
extension requires a secure local context, opt in to HTTPS with certificates
outside the repository:
DEV_HTTPS_KEY_PATH=/absolute/path/localhost-key.pem \
DEV_HTTPS_CERT_PATH=/absolute/path/localhost.pem \
npm run devBoth variables are required together. The normal development and production build commands do not read certificate files.
Blast relays are application-controlled publication destinations. Configure the single canonical inventory in src/utils/templates.js; the apply preview enumerates that whole inventory separately from the shared template's main relay destinations:
export const BLAST_RELAYS = freezeArray([
'wss://relay.primal.net', 'wss://relay.damus.io',
'wss://relay.ditto.pub', 'wss://offchain.pub',
'wss://sendit.nosflare.com', 'wss://nostr.mom',
'wss://nos.lol', 'wss://purplepag.es',
'wss://indexer.coracle.social', 'wss://user.kindpag.es',
'wss://directory.yabu.me', 'wss://profiles.nostr1.com'
].map((url) => ({ url: canonicalizeEndpoint(url, 'relay') })));Publication uses the union of those configured blast destinations and the canonical template's main relays. A destination shown in both groups is contacted once, and one apply operation opens at most four WebSocket connections across all event kinds combined.
Add or modify community templates in src/utils/templates.js:
const myCommunity = canonicalizeTemplate({
id: 'my-community',
name: 'My Community',
description: 'Official relay set for My Community',
relays: [
{ url: 'wss://relay.mycommunity.com', read: true, write: true },
{ url: 'wss://relay.damus.io', read: true, write: true }
],
blossomServers: [{ url: 'https://cdn.mycommunity.com' }],
dmRelays: [{ url: 'wss://dm.mycommunity.com' }]
});
export const getCommunityTemplates = () => Object.freeze({
'my-community': myCommunity
});Keep every preset behind canonicalizeTemplate; it returns the same deeply
frozen, bounded shape used for decoded shared links.
The creator draft defaults and its immutable reducer live in src/components/templateCreatorState.js. Keep default rows within TEMPLATE_LIMITS in src/utils/templates.js; the reducer and accessible Add controls enforce those same limits before canonical validation on submit.
The Dyne-styled VitePress website lives in docs/ and is deployed to GitHub Pages by .github/workflows/deploy-docs.yml.
cd docs
npm ci
npm run buildFor the repository Pages path, build with BASE_PATH=/communitator/. In GitHub repository settings, set Pages → Build and deployment → Source to GitHub Actions.
# Build the webapp
npm run build
This creates a dist folder with all static assets ready for deployment.
Upload the contents of the dist folder to any static web host (Netlify, Vercel, Cloudflare Pages, etc.).
npm run preview is useful only for locally inspecting the already-built
assets; it is not a production server. Use a static host or a configured web
server to serve dist/ in production.
#Deploy to your server
rsync -av dist/ user@remote.server.tld:/path/to/webroot/server {
listen 443 ssl http2;
server_name yourdomain.com;
root /var/www/communitator/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' wss:; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;
}These are Nostr event-kind identifiers, not TCP ports or NIP numbers.
| Nostr event kind | Description | Defined by | Published To |
|---|---|---|---|
| 10002 | User's main relays | NIP-65 | Deduplicated union of configured blast and reviewed template relays |
| 10063 | Blossom servers | NIP-B7 | Deduplicated union of configured blast and reviewed template relays |
| 10050 | DM relays | NIP-17 | Deduplicated union of configured blast and reviewed template relays |
- HTTPS: Nostr extensions require a secure context
- Nostr Extension: Alby, Nos2x, or any NIP-07 compatible extension
- Modern Browser: Chrome, Firefox, Safari (latest versions)
The app works with any NIP-07 compatible Nostr extension:
Shared links are untrusted recommendations. The app accepts only bounded, versioned canonical template links and shows normalized endpoints, read/write permissions, the exact event kinds, every configured blast destination, and every canonical template relay destination before any signer interaction. Connect requests an identity; Apply separately asks the extension to sign kind 10002, and kinds 10063/10050 only when present. Signed events are sent to the deduplicated union of the two displayed destination groups, with at most four sockets across the operation; a relay counts only after a positive NIP-01 OK, so results can be complete, partial, failed, or cancelled. Public relay contact has privacy implications: operators can observe your network address and the event you publish.
See SECURITY.md for the hosting header contract, residual GitHub Pages limitations, disclosure process, and release checklist.
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
GNU GENERAL PUBLIC LICENSE Version 3