Email notifications for Nostr private groups
Find a file
Agent 25645e1d0e Fix unsubscribe page path, remove pool override, handle Postmark error gracefully
- Fix unsubscribe page path (unsubscribe-success.html → unsubscribe.html)
- Remove netContext.pool.get override that crashed load()
- Handle Postmark registration error gracefully by returning subscription from DB
- Add getSubscriptionByPubkey import to server.ts
2026-08-18 12:26:07 -04:00
src Fix unsubscribe page path, remove pool override, handle Postmark error gracefully 2026-08-18 12:26:07 -04:00
web Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
.env.template Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
.gitattributes Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
.gitignore Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
.nvmrc Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
.prettierignore Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
.prettierrc Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
build-in-production.sh Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
eslint.config.mjs Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
LICENSE Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
package.json Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
pnpm-lock.yaml Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
pnpm-workspace.yaml Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
README.md Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
remove-pnpm-overrides.js Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00
tsconfig.json Initial mailship fork from anchor 2026-08-18 12:21:22 -04:00

Mailship

A Nostr email notification server. Receives events pushed from relays via NIP-9a, stores them, and sends daily or weekly digest emails.

Architecture

Flotilla ──HTTP──▶ Mailship (POST /subscription/email)
  │                   NIP-98 auth
  │                   returns {key, callback}
  │
  └──kind 30390──▶ relay or NPB
                         │
                    event matches filter
                         │
                         ▼
                    POST /notify/:subId ──▶ Mailship
                         {id, relay}         │
                           UUID in path      ├── fetch event from relay
                           is the auth       ├── store in SQLite (dedup)
                                             │
                                        cron fires ──▶ render digest ──▶ Postmark ──▶ email

Configuration

Variable Required Description
MAILSHIP_SECRET ✓ A nostr private key hex string for the server's identity
MAILSHIP_NAME ✓ Name of this Mailship instance
MAILSHIP_URL ✓ Public URL of this instance
BASE_URL ✓ Base URL for callback URLs (same as MAILSHIP_URL typically)
POSTMARK_API_KEY ✓ Postmark API key for sending emails
POSTMARK_SENDER_ADDRESS ✓ Verified sender email in Postmark
DEFAULT_RELAYS ✓ Comma-separated list of default relays
INDEXER_RELAYS ✓ Comma-separated list of indexer relays
SEARCH_RELAYS ✓ Comma-separated list of search relays
PORT Port to run on (default: 3000)

API

POST /subscription/email

Register for email digests.

Body: { email, frequency, pubkey }
Auth: NIP-98 (planned)
Response: { key, callback }

DELETE /subscription/:key

Unsubscribe.

Auth: NIP-98 (planned)
Response: { ok: true }

POST /notify/:id

NIP-9a relay push callback. Called by relays or NPB when matching events are found.

Body: { id, relay }
Response: { ok: true, stored: boolean }
Returns 404 if subscription not found or inactive.

GET /confirm?token=...

Confirm email address via link from confirmation email.

GET /unsubscribe?token=...

Unsubscribe via link from digest email.

Development

pnpm install
pnpm run build
pnpm run start

Forked from Anchor

Mailship is a fork of Anchor, stripped of push notification support and adapted for NIP-9a relay push event intake.