A social media post scheduler with a calendar view, multi-channel composer, queue management, and analytics. Built with Preact + Tailwind CSS + Hono + D1. Deploys to Cloudflare Workers via Clawnify.
Think of it as an open-source alternative to Buffer, Hypefury, Typefully, or Twitter Hunter -- a complete content scheduling system you can self-host and customize.
- Multi-channel composer -- write once, publish to multiple platforms with per-platform character limits
- Calendar view -- month grid showing scheduled posts with platform color dots
- Queue management -- chronological list of scheduled posts with one-click publish
- Drafts -- save unfinished posts and come back to them later
- Channel management -- add your social media accounts with platform detection and color coding
- Labels -- categorize posts with colored labels for organization
- Media attachments -- add image URLs to posts
- Analytics -- bar charts showing posts per channel, per label, and daily activity
- Dashboard -- at-a-glance stats, upcoming posts, and recent drafts
- Twitter/X publishing -- publish directly to X via Clawnify credential service binding
- URL routing -- bookmarkable pages (
/compose,/calendar,/queue,/drafts,/channels,/analytics)
| Platform | Character Limit | Publishing |
|---|---|---|
| X / Twitter | 280 | via Clawnify credentials |
| 3,000 | coming soon | |
| 2,200 | coming soon | |
| 63,206 | coming soon | |
| Bluesky | 300 | coming soon |
| Mastodon | 500 | coming soon |
| Threads | 500 | coming soon |
| TikTok | 2,200 | coming soon |
git clone https://github.com/clawnify/open-post.git
cd open-post
pnpm installStart the dev server:
pnpm devOpen http://localhost:5173 in your browser. The database schema is applied automatically on startup.
For local dev, create a .dev.vars file with OAuth 1.0a keys from the X Developer Portal:
TWITTER_CONSUMER_KEY=your-api-key
TWITTER_CONSUMER_SECRET=your-api-secret
TWITTER_ACCESS_TOKEN=your-access-token
TWITTER_ACCESS_TOKEN_SECRET=your-access-token-secret
In production (deployed via Clawnify), credentials are managed automatically through Clawnify's credential service binding -- no API keys needed in the app.
| Layer | Technology |
|---|---|
| Frontend | Preact, TypeScript, Tailwind CSS v4, Vite |
| Backend | Hono (Cloudflare Worker) |
| Database | D1 (SQLite at the edge) |
| Icons | Lucide |
| Credentials | Clawnify credential service binding (Composio OAuth) |
- Node.js 20+
- pnpm
src/
server/
index.ts -- Hono API with D1 + credentials middleware
db.ts -- D1-native database adapter
credentials.ts -- Credential service binding adapter (prod + local fallback)
twitter.ts -- X API v2 client (OAuth 2.0 Bearer + OAuth 1.0a)
schema.sql -- Database schema (channels, posts, labels, media)
client/
app.tsx -- Root component with router
context.tsx -- Preact context for app state
hooks/
use-app.ts -- State management + CRUD operations
use-router.ts -- pushState URL router
components/
sidebar.tsx -- Navigation sidebar
dashboard.tsx -- Stats cards + upcoming posts
post-composer.tsx -- Multi-channel post editor with char limits
calendar-view.tsx -- Month grid calendar
queue-view.tsx -- Scheduled posts queue
drafts-view.tsx -- Draft posts list
channel-list.tsx -- Channel management
analytics-view.tsx -- Bar charts and daily activity
post-card.tsx -- Reusable post preview card
error-banner.tsx -- Error display
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/channels |
List channels |
| POST | /api/channels |
Create channel |
| PUT | /api/channels/:id |
Update channel |
| DELETE | /api/channels/:id |
Delete channel |
| GET | /api/labels |
List labels |
| POST | /api/labels |
Create label |
| PUT | /api/labels/:id |
Update label |
| DELETE | /api/labels/:id |
Delete label |
| GET | /api/posts |
List posts (filterable by status, channel, date range) |
| GET | /api/posts/:id |
Get post with channels, labels, media |
| POST | /api/posts |
Create post |
| PUT | /api/posts/:id |
Update post |
| DELETE | /api/posts/:id |
Delete post |
| GET | /api/posts/calendar?month=YYYY-MM |
Posts grouped by day for calendar |
| POST | /api/posts/:id/publish |
Publish post to assigned channels |
| GET | /api/stats |
Dashboard stats + per-channel/label breakdowns |
In production, the app uses Cloudflare Service Bindings to fetch fresh OAuth tokens from Clawnify's central credential Worker -- zero network hop, no secrets stored on the app.
App Worker --(RPC)--> CredentialService.getToken("twitter", orgId)
--> Composio (auto-refresh)
--> Fresh Bearer token
--> X API v2
See credentials-service-binding.md for the full architecture.
npx clawnify deployMIT