Filaxy Herald

June 24, 2026 · View on GitHub

Filaxy Herald

Your GitHub work → posts on X, automatically.

Filaxy Herald watches your public repos. When you ship something, an AI writes a real build-in-public post about it, makes a branded image, and sends it to your Telegram. You tap ✅ — and it's live on X. You also get an /announce command for private projects.

No "I pushed 3 commits" spam. Real posts. You approve every one. It runs itself.

How it works

Self-hosted · Runs free on Vercel · MIT


See it in action

When you ship code, you get a message like this on Telegram. One tap and it's live — no dashboards, no copy-paste:

Telegram approval — tap to publish

↓ It really posts. See a real post it published on X →


Table of contents


What it does

There are two ways posts get created:

A — Automatic (your public repos). Once a day, Herald reads your public GitHub activity. If you shipped something worth sharing (a release, a new repo, or real commits to main), it drafts a post and sends it to you on Telegram for approval. It posts one suggestion per repo, so you approve or discard each independently.

B — Manual (/announce, for anything — even private projects). Send the bot a message like /announce my-app: shipped a live dashboard with real-time charts. It writes a polished post from your description (never from your code, so nothing private leaks). Attach a screenshot and it uses your real image instead of the generated card.

Every post goes through a safety filter (no API keys, secrets, or security bugs) and you approve it on Telegram before it ever reaches X.


How it works (architecture)

You ship code ─▶ GitHub
                   │  (public activity feed — read once a day, no private repos)

            ┌──────────────┐    "write the post"     ┌──────────┐
            │  The Engine  │ ──────────────────────▶ │  Claude  │
            │   (Vercel)   │ ◀────────────────────── │    AI    │
            └──────┬───────┘     the draft            └──────────┘

                   │  ① safety check (block secrets / bugs)
                   │  ② render a branded "ship card" image
                   │  ③ store the draft        ┌──────────┐
                   │ ─────────────────────────▶│  Upstash │ (memory)
                   ▼                            └──────────┘
              Telegram  ──(you tap ✅ Approve)──▶  X / Twitter  ──▶  your audience grows
  • The Engine is a few serverless functions on Vercel. A daily cron triggers the digest; a webhook receives your Telegram button taps.
  • It reads your activity from GitHub's public events feed — no GitHub App, no webhook on GitHub's side, and private repos physically can't appear there.
  • Claude writes the copy. Upstash Redis holds the pending draft between "drafted" and "approved". Telegram is your approval inbox. X is the destination.
FileWhat it is
api/digest.tsDaily cron: read activity → write → image → send to Telegram
api/telegram-webhook.tsHandles your ✅/❌ taps and the /announce command → posts to X
api/announce.tsBuilds a post from your /announce description
lib/github.tsReads & filters your public activity
lib/compose.tsPrompts Claude to write the post
lib/safety.tsThe content guardrail (regex + AI review)
lib/card-og.tsRenders the branded image
lib/redis.ts · lib/telegram.tsMemory + Telegram helpers

Prerequisites

You'll need free accounts on these (all have free tiers; two need a few dollars of prepaid credit):

ServiceWhyCost
GitHubthe activity it readsfree
Telegramyour approval inboxfree
AnthropicClaude writes the posts~$5 prepaid lasts months
X / Twitter Developerpublishingpay-per-use, ~$0.02/post
UpstashRedis memoryfree tier
Vercelhostingfree (Hobby)

Also install Node.js 18+ and the Vercel CLI: npm i -g vercel.

Then clone and install:

git clone https://github.com/othmarodev/filaxy-herald.git
cd filaxy-herald
npm install
cp .env.example .env.local

You'll fill .env.local as you go through the steps below. It's git-ignored — your keys never get committed.


Setup — step by step

1. Telegram bot (2 min)

This is where you'll approve posts.

  1. Open Telegram, search for @BotFather (the one with the blue checkmark).
  2. Send /newbot. Give it a name, then a username ending in bot (e.g. my_herald_bot).
  3. BotFather replies with a token like 8123456789:AAH.... → TELEGRAM_BOT_TOKEN.
  4. Now get your chat ID: search @userinfobot, press Start. It replies with your Id (a number). → TELEGRAM_CHAT_ID.
  5. Open your own bot (t.me/your_bot_username) and press Start once. (A bot can't message you until you've messaged it first — skip this and you'll get "chat not found".)

Test it:

npx tsx scripts/test-telegram.ts   # should send you a message

2. Anthropic (Claude) API key (5 min)

This writes your posts.

  1. Go to console.anthropic.com, sign in.
  2. API KeysCreate Key → name it filaxy-herald. Copy the sk-ant-... value (shown once). → ANTHROPIC_API_KEY.
  3. Plans & BillingBuy credits → add ~$5. (The free "evaluation" plan has no usable credits — the key won't work until you add a little balance.)
npx tsx scripts/test-compose.ts    # generates a real sample post

3. X / Twitter developer app (15 min)

The fiddly one. Read carefully — there's one step that trips everyone.

  1. Go to developer.x.com, sign in with the account you'll post from. Accept the Free developer plan.
  2. You'll get a Project + an App. Open the App.
  3. 🚨 Before generating any tokens: open User authentication settings → Set up:
    • App permissionsRead and Write. (If you leave it on "Read", your tokens will be read-only and posting fails. This is THE gotcha.)
    • Type of AppWeb App / Automated App or Bot.
    • Callback URIhttps://example.com/callback (placeholder is fine).
    • Website URL → your site (anything valid).
    • Save.
  4. Now go to Keys and tokens and grab four values:
    • API Key + API Key SecretX_API_KEY, X_API_SECRET
    • Access Token + Access Token Secret (Generate) → X_ACCESS_TOKEN, X_ACCESS_SECRET. Confirm it says Read and Write under the token. If it says "Read", fix step 3 and regenerate.
  5. The new X API is pay-per-use. Add a few dollars of credits in the developer console so it can publish.
npx tsx scripts/test-x-auth.ts     # verifies the 4 keys (without posting)

4. Upstash Redis (3 min)

The bot's short-term memory (holds a draft between "written" and "approved").

  1. Go to console.upstash.com, create a Redis database (free tier, pick a region).
  2. In the database page, find the REST API section. Copy:
    • UPSTASH_REDIS_REST_URL
    • UPSTASH_REDIS_REST_TOKEN

5. Deploy to Vercel (5 min)

vercel login
vercel link --yes          # creates the project

Add your environment variables to the Vercel project (Production). Either paste them in the Vercel dashboard (Project → Settings → Environment Variables) or via CLI:

# for each variable: pipe the value from .env.local so it's never printed
grep '^TELEGRAM_BOT_TOKEN=' .env.local | cut -d= -f2- | vercel env add TELEGRAM_BOT_TOKEN production
# ...repeat for every var in .env.example...

Two extra variables to add:

# stops Vercel from downloading a browser it doesn't need in the cloud
printf '1' | vercel env add PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD production
# protects your cron endpoint (Vercel sends this automatically to the cron)
printf '%s' "$(openssl rand -hex 16)" | vercel env add CRON_SECRET production
# lets the webhook call the announce endpoint internally
printf '%s' "$(openssl rand -hex 16)" | vercel env add INTERNAL_SECRET production

Deploy:

vercel --prod

The daily cron is configured in vercel.json (0 15 * * * = 09:00 in UTC-6 / Costa Rica — change it to your timezone).

6. Connect the Telegram webhook (1 min)

So your button taps reach the cloud. Edit the URL in scripts/set-webhook.ts to your deployed domain, then:

npx tsx scripts/set-webhook.ts

Done. 🎉


Configuration

All in .env.local (and mirrored in your Vercel project). See .env.example.

VariableWhat it is
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_IDyour approval bot + your chat
ANTHROPIC_API_KEYClaude (writes posts + runs the AI safety check)
X_API_KEY / X_API_SECRET / X_ACCESS_TOKEN / X_ACCESS_SECRETX OAuth 1.0a (Read+Write)
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKENmemory
GITHUB_USERthe GitHub username to watch
ALLOWLISTcomma-separated owner/repo. Leave empty = all your public repos (skips your profile repo and forks).
CRON_SECRET / INTERNAL_SECRETendpoint protection (random strings)

Usage

  • Automatic: just ship code to your public repos. Each day at your cron time, Herald DMs you a draft per repo that had activity. Tap ✅ to post, ❌ to skip.
  • Manual: message your bot /announce <project>: <what you shipped>. Works for any project, public or private. Attach a photo to use a real screenshot instead of the generated card. Write in any language — the post comes out in English.

Safety: it never posts secrets

Every post — automatic or manual — runs through lib/safety.ts before it can reach approval:

  1. Regex backstop — instantly blocks anything matching an API key, token, private key, Stripe/AWS/GitHub secret, or a security-bug disclosure.
  2. AI reviewer — Claude flags anything sensitive the regex might miss (secrets, vulnerabilities, private data, brand-damaging content). Fails closed (blocks on doubt).

And of course: it only ever reads your public GitHub feed, and /announce posts from your description, never your code. Private repos can't leak.

npx tsx scripts/test-safety.ts     # see the guardrail block fake secrets

What it costs

ItemPer post
X (publish)~$0.02
Claude (writing + safety check)< $0.01
Branded image / screenshot$0
Hosting (Vercel Hobby)$0
Total~2–3 cents

One post a day ≈ under $1/month. A $5 X balance ≈ 250 posts. Set a spending limit in the X console for peace of mind.


Troubleshooting

SymptomFix
Telegram: "chat not found"Open your bot and press Start once (step 1.5).
Anthropic: "credit balance too low"Add ~$5 in Plans & Billing — the free plan has no credits.
X: tokens are read-only / posting 403Set Read and Write in User auth settings, then regenerate the access token.
X: "CreditsDepleted"Add credits in the X developer console (pay-per-use).
Webhook returns 500 / ERR_MODULE_NOT_FOUNDVercel Node functions need self-contained imports — keep api/telegram-webhook.ts dependency-light (it is, by design).
Cron runs but nothing postsThat's correct if you had no post-worthy public activity that day — it never invents posts.
The /announce post links the wrong placePrivate projects link to your GitHub profile by design (never the private repo).

License

MIT © Othmaro Fallas Rojas — @Othmaro_dev · github.com/othmarodev

Built in public. This bot announces its own updates. 🤖