A modern blog platform inspired by Posterous that allows publishing posts via email.
Your email blog platform is fully functional! Send an email to start publishing.
- 📧 Email-to-Publish: Send an email to publish posts instantly
- 🔒 Email Security: Allowlist and verification system to prevent unauthorized posts
- 🎨 Multi-format Support: HTML and plain text posts
- 📡 RSS Feed: Full RSS 2.0 support for syndication
- 🌐 WebSub: Real-time feed updates via WebSub protocol (configured)
- 🐘 ActivityPub: Full federated social networking with followers, likes, and shares ✅
- 🦋 AT Protocol: Bluesky integration for cross-platform syndication (configured)
- 🔗 SEO Friendly: Clean URLs with slugified titles
- 📱 Responsive Design: Mobile-first responsive interface with TailwindCSS
- ⚡ Fast: Built on Val Town with SQLite storage and static HTML generation
- 🚀 No JavaScript: Pure HTML/CSS frontend for maximum performance
index.ts
- Main Hono server with HTML generation and API routes ✅database/
- SQLite schema and query functions ✅services/
- External service integrations ✅
- Email trigger handler with security verification ✅
- Allowlist checking and draft post creation ✅
- Automated verification email sending ✅
- Server-side HTML rendering with TailwindCSS ✅
- No client-side JavaScript dependencies ✅
- Fast loading and SEO optimized ✅
- Configure Security: Set
ALLOWED_EMAIL_ADDRESSES
environment variable (see Security section) - Send Email: Send email from an allowed address to your Val Town email address
- Verify Email: Click the verification link sent to your email
- View Blog: Visit your backend HTTP val URL after verification
- RSS Feed: Access
/rss
for syndication - Individual Posts: Visit
/post/[slug]
See SETUP.md for detailed setup instructions and ACTIVITYPUB.md for ActivityPub federation details.
ALLOWED_EMAIL_ADDRESSES
- Comma-separated list of allowed email addresses (required)BASE_URL
- Your blog's base URL (e.g., https://myblog.com or just myblog.com) - Required for custom domains and ActivityPub federation
WEBSUB_HUB_URL
- WebSub hub URLACTIVITYPUB_DOMAIN
- Domain for ActivityPub federation (deprecated - use BASE_URL instead)ATPROTO_HANDLE
- AT Protocol handleATPROTO_PASSWORD
- AT Protocol app password
To use a custom domain with ActivityPub federation:
- Set up your custom domain in Val Town (see Val Town documentation)
- Set the BASE_URL environment variable to your custom domain:
BASE_URL=https://yourdomain.com
(with protocol)- OR
BASE_URL=yourdomain.com
(protocol will be assumed as https)
- Test ActivityPub discovery by visiting
https://yourdomain.com/.well-known/webfinger?resource=acct:blog@yourdomain.com
Without BASE_URL set, the system will use the default Val Town URL for ActivityPub federation.
GET /
- Main blog interface (with ActivityPub Link header)GET /post/:slug
- Individual post page (supports content negotiation for ActivityPub)GET /rss
- RSS 2.0 feedGET /api/posts
- JSON API for postsGET /api/posts/:slug
- JSON API for single postGET /websub
- WebSub subscription endpointGET /.well-known/webfinger
- WebFinger discovery for ActivityPub ✅GET /actor
- ActivityPub actor document with rich metadata ✅GET /outbox
- ActivityPub outbox (paginated published activities) ✅GET /outbox?page=N
- Paginated outbox pages ✅GET /followers
- ActivityPub followers collection (with pagination support) ✅GET /followers-list
- Human-readable followers page ✅GET /api/followers
- JSON API for followers list ✅GET /following
- ActivityPub following collection ✅POST /inbox
- ActivityPub inbox (processes Follow, Like, Announce, Undo) ✅GET /api/posts/:slug/activities
- Get activity counts (likes, shares, replies) ✅GET /verify-email
- Email verification endpoint ✅GET /health
- Health check
Only emails from pre-configured addresses can publish posts. Configure via environment variable:
ALLOWED_EMAIL_ADDRESSES=user1@example.com,user2@example.com,admin@myblog.com
To prevent spoofing, all emails go through a verification process:
- Email Received: Email is stored as a draft (not published)
- Verification Sent: Automated verification email sent to sender
- User Clicks Link: Sender clicks verification link in email
- Post Published: After verification, post is published and syndicated
- Anti-spoofing: Prevents unauthorized users from publishing posts
- Email confirmation: Ensures the sender actually sent the email
- Time-limited: Verification links expire after 24 hours
- Automatic cleanup: Expired drafts are automatically removed
Use /test-publish.ts
to create sample posts for testing.
Send an email like this:
To: your-email-val@val.town
From: allowed-user@example.com (must be in ALLOWED_EMAIL_ADDRESSES)
Subject: My Amazing Blog Post
Body: <h2>Hello World!</h2><p>This post was published via email!</p>
The process will be:
- Email received and stored as draft
- Verification email sent to
allowed-user@example.com
- User clicks verification link
- Post published with:
- Title: "My Amazing Blog Post"
- Slug: "my-amazing-blog-post"
- Content: Rendered HTML
- Author: Extracted from email address
- Edit HTML generation functions in
/backend/index.ts
- Modify CSS styles in the
getCustomCSS()
function - Update branding in HTML templates
- Configure federation services via environment variables
Posts are automatically syndicated to:
- RSS feed (always enabled)
- WebSub subscribers (if configured)
- ActivityPub followers (with full interaction support) ✅
- AT Protocol/Bluesky (if configured)
Your blog is now fully federated with ActivityPub! Users can:
- Follow your blog from Mastodon, Pleroma, and other ActivityPub platforms
- Like your blog posts (shows ❤️ count on posts)
- Share/Boost your posts (shows 🔄 count on posts)
- Reply to your posts (shows 💬 count on posts)
- Preview posts directly in Mastodon timeline with proper formatting
Discovery formats:
- WebFinger:
@blog@your-domain.com
- Direct actor URL:
https://your-domain.com/actor
Configuration for custom domains:
- Set
BASE_URL=https://your-custom-domain.com
in your environment variables - This ensures all ActivityPub URLs use your custom domain instead of the default Val Town URL
Enhanced ActivityPub Features:
- ✅ Paginated Outbox: Posts are served with proper pagination for better performance
- ✅ Rich Post Previews: Posts display with titles, summaries, and full content in Mastodon
- ✅ Content Negotiation: Individual posts serve both HTML and ActivityPub JSON based on Accept headers
- ✅ Profile Metadata: Actor profile includes avatar, header image, and custom fields
- ✅ Link Discovery: Proper HTTP Link headers for ActivityPub discovery
- ✅ Post Permalinks: Each post has its own ActivityPub Note endpoint
Real-time interaction tracking:
- All likes, shares, and replies are stored and displayed on individual post pages
- Follower count is maintained and accurate
- Full ActivityPub inbox processing for Follow/Unfollow activities
Example: If your blog is at myblog.com
(with BASE_URL=https://myblog.com
), users can follow @blog@myblog.com
from their Mastodon client and see your posts with rich previews in their timeline!
Ready to blog via email? Send your first post now! 📧✨