A self-hosted scheduling and cross-posting tool for FetLife. Schedule status posts, write group cross-posts, fan one event announcement out to multiple groups with automatic staggered timing, and manage multiple accounts from a single dashboard.
Disclaimer: This tool automates a service whose Terms of Service may prohibit automation. Use it for your own accounts, at your own risk. Don't spam, don't post on behalf of others without consent, and respect rate limits. The author is not affiliated with FetLife.
- Schedule status posts for any time in the future, per account.
- Group cross-post — author one event announcement and fan it out to a list of FetLife groups with a 60–120s stagger between posts (avoids looking automated).
- Multi-account support — run posts under several FetLife identities from one UI.
- Calendar view — see what's scheduled across all accounts, click any chip to inspect or cancel a post.
- Saved templates scoped per account/per group for posts you write often.
- Auto-discovery of your joined groups and organized/attending events for quick selection.
- Image attachments with thumbnail previews in the calendar and queue.
- Canva integration (optional) — pick a design directly from your Canva account, FetPost exports it as PNG and attaches it to the post.
- Cookie-based authentication — log in once via a real browser, then the bot uses session cookies for all subsequent posting (no headless password-login race against Cloudflare).
Two Node.js services run side by side, communicating over localhost HTTP:
Browser ─► nexuspost-ui (port 4000) ─HTTP─► fetlife-poster (port 3747)
│ │
│ ├── credentials.js (AES-256-GCM)
│ ├── scheduler.js (persistent timer queue)
│ ├── poster.js (Playwright + real Chrome)
│ └── extractor.js (cookie capture)
└── public/index.html (single-page UI)
nexuspost-ui/— Express + a single-page HTML dashboard.fetlife-poster/— Playwright-based automation service. Bound to127.0.0.1only.
- Node.js ≥ 20.6 (for
--env-filesupport) - Google Chrome (Playwright drives a real Chrome, not Chromium, to avoid bot detection)
git clone https://github.com/<your-username>/FetPost.git
cd FetPost
# Copy the env template and generate secrets
cp .env.example .env
node -e "console.log('FL_SERVICE_SECRET=' + require('crypto').randomBytes(32).toString('hex'))" >> .env
node -e "console.log('FL_MACHINE_SECRET=' + require('crypto').randomBytes(32).toString('hex'))" >> .env
# Edit .env and remove the placeholder values
# Install service dependencies
(cd fetlife-poster && npm install)
(cd nexuspost-ui && npm install)Linux / macOS:
./start.shWindows:
start.cmdOpen http://localhost:4000 in your browser. Add a FetLife account; cookie capture will open a Chrome window where you log in once, and from there scheduling and posting works headless.
FetLife sits behind Cloudflare, which actively blocks automated logins from datacenter IPs and challenges any login that looks scripted. Rather than play that arms race, FetPost captures session cookies through a single human login:
- You click Add Account in the UI.
- A real Chrome window opens (visible —
headless: false). - You log in normally. Solve any Cloudflare challenge or 2FA prompt.
- Once logged in, you click "I've logged in" in the UI (or hit Enter in the spawning terminal).
- FetPost saves the cookies. All future posts use those cookies — no password login, no Cloudflare challenge.
Cookies typically last 1–4 weeks. A scheduled cron job at 4 AM every other day attempts a silent headless refresh (revisit /home, FetLife rotates cookies, re-save) to extend their life. When the cookies fully expire, the cron fails and you redo the manual login flow.
If you create your post images in Canva, FetPost can pull designs straight from your account.
-
Go to https://www.canva.com/developers/ and create a new integration.
-
Add a redirect URL matching where FetPost runs — typically
http://localhost:4000/oauth/canva/callback(local) orhttp://<tailscale-name>:4000/oauth/canva/callback(server). -
Add scopes
design:meta:readanddesign:content:read. -
Copy the Client ID and Client Secret into your
.env:CANVA_CLIENT_ID=your-client-id CANVA_CLIENT_SECRET=your-client-secret CANVA_REDIRECT_URI=http://localhost:4000/oauth/canva/callback -
Restart FetPost. The Accounts tab will now show a "🎨 Canva Integration" card. Click Connect Canva, approve in the browser tab that opens, and you're set.
-
In Compose with Image post type, you'll see a Pick from Canva button. Click it to browse your designs and import one as the post image.
Note: Canva may require HTTPS for non-localhost redirect URIs. If your registration is rejected, set up TLS on your server (Caddy + Let's Encrypt is easiest) and use the https:// URL.
For the bot to fire scheduled posts when your laptop is off, run FetPost on a small server. The author runs it on a $6/month DigitalOcean Droplet:
- Tailscale for private remote access from any device (UI is bound to
127.0.0.1; Tailscale tunnels in). - NordVPN on the server to mask the datacenter IP from Cloudflare (residential-style VPN exits don't trigger as many challenges).
- systemd unit for auto-start on boot.
- TigerVNC + XFCE for occasional manual cookie refreshes.
See DEPLOYMENT.md for the full setup walkthrough. (Coming soon — for now, the gist is: install Node + Chrome + Tailscale + NordVPN + TigerVNC, scp the repo, point the UI at http://<tailscale-name>:4000.)
- Credentials are encrypted at rest with AES-256-GCM. The encryption key is the
FL_MACHINE_SECRETenv var, derived per machine. - Both services bind to
127.0.0.1only — the UI service has no external listener by default. - Inter-service HTTP requests carry a shared secret (
FL_SERVICE_SECRET) header. data/is in.gitignore— credentials, cookies, scheduled posts, and post history never leave your machine.- The default deployment exposes the UI through Tailscale (private tailnet only). Don't put
:4000on the public internet.
Built and used in production by the author since early 2026. Active development is focused on FetLife. Earlier versions targeted Bluesky / OnlyFans / Fansly / ManyVids / NiteFlirt; that code has been removed but git history may still show traces of it.
Issues and PRs welcome. Please don't file anything that would help someone abuse the tool (e.g., evading FetLife's rate limits, posting on behalf of accounts you don't own). The README is in plain English on purpose — if something's confusing, that's a bug.
MIT — see LICENSE.