Email notifications for Nostr private groups
Find a file
Agent f220301154 Add integration test script, fix root handler, add DATA_DIR support
- test/integration.sh: runs full E2E flow against fresh server
- pnpm test and pnpm test:server scripts added
- Root endpoint handles missing web UI gracefully (returns JSON)
- database.ts reads DATA_DIR env var for test isolation
- .gitignore updated for test artifacts
2026-08-18 12:35:20 -04:00
src Add integration test script, fix root handler, add DATA_DIR support 2026-08-18 12:35:20 -04:00
test Add integration test script, fix root handler, add DATA_DIR support 2026-08-18 12:35:20 -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 Add integration test script, fix root handler, add DATA_DIR support 2026-08-18 12:35:20 -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 Add integration test script, fix root handler, add DATA_DIR support 2026-08-18 12:35:20 -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.