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.
Self-hosted
· Runs free on Vercel
· MIT
When you ship code, you get a message like this on Telegram. One tap and it's live — no dashboards, no copy-paste:
↓ It really posts. See a real post it published on X →
What it doesHow it works (architecture)PrerequisitesSetup — step by stepConfigurationUsageSafety: it never posts secretsWhat it costsTroubleshootingLicense
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.
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 dailycron triggers the digest; awebhook 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.
| File | What it is |
|---|---|
api/digest.ts |
|
| Daily cron: read activity → write → image → send to Telegram | |
api/telegram-webhook.ts |
|
Handles your ✅/❌ taps and the /announce command → posts to X |
|
api/announce.ts |
|
Builds a post from your /announce description |
|
lib/github.ts |
|
| Reads & filters your public activity | |
lib/compose.ts |
|
| Prompts Claude to write the post | |
lib/safety.ts |
|
| The content guardrail (regex + AI review) | |
lib/card-og.ts |
|
| Renders the branded image | |
lib/redis.ts · lib/telegram.ts |
|
| Memory + Telegram helpers |
You'll need free accounts on these (all have free tiers; two need a few dollars of prepaid credit):
| Service | Why | Cost |
|---|---|---|
TelegramAnthropic$5 prepaid lasts monthsX / Twitter Developer~$0.02/postUpstashVercelAlso 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.
This is where you'll approve posts.
- Open Telegram, search for
@BotFather(the one with the blue checkmark). - Send
/newbot
. Give it a name, then a username ending inbot
(e.g.my_herald_bot
). - BotFather replies with a
token like8123456789:AAH...
. →TELEGRAM_BOT_TOKEN
. - Now get your chat ID: search
@userinfobot, press** Start**. It replies with yourId
(a number). →TELEGRAM_CHAT_ID
. Open your own bot(t.me/your_bot_username
) and pressStart 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
This writes your posts.
- Go to
console.anthropic.com, sign in. API Keys→** Create Key**→ name it
filaxy-herald
. Copy thesk-ant-...
value (shown once). →ANTHROPIC_API_KEY
.Plans & Billing→** Buy 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
The fiddly one. Read carefully — there's one step that trips everyone.
- Go to developer.x.com, sign in with the account you'll post from. Accept theFree developer plan. - You'll get a Project + an App. Open the App.
- 🚨
Before generating any tokens: openUser authentication settings → Set up:** App permissions→ Read and Write**.*(If you leave it on "Read", your tokens will be read-only and posting fails. This is THE gotcha.)Type of App→* Web App / Automated App or Bot**.** Callback URI**→
https://example.com/callback
(placeholder is fine).Website URL→ your site (anything valid).- Save.
- Now go to
Keys and tokens and grabfour values:API Key+** API Key Secret**→
X_API_KEY
,X_API_SECRET
Access Token+** Access Token Secret**(Generate) →X_ACCESS_TOKEN
,X_ACCESS_SECRET
. Confirm it saysRead and Write under the token. If it says "Read", fix step 3 and regenerate.
- 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)
The bot's short-term memory (holds a draft between "written" and "approved").
- Go to
console.upstash.com, create aRedis database (free tier, pick a region). - In the database page, find the
REST API section. Copy:
UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN
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:
grep '^TELEGRAM_BOT_TOKEN=' .env.local | cut -d= -f2- | vercel env add TELEGRAM_BOT_TOKEN production
Two extra variables to add:
printf '1' | vercel env add PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD production
printf '%s' "$(openssl rand -hex 16)" | vercel env add CRON_SECRET production
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).
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. 🎉
All in .env.local
(and mirrored in your Vercel project). See .env.example
.
| Variable | What it is |
|---|---|
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID |
|
| your approval bot + your chat | |
ANTHROPIC_API_KEY |
|
| Claude (writes posts + runs the AI safety check) | |
X_API_KEY / X_API_SECRET / X_ACCESS_TOKEN / X_ACCESS_SECRET |
|
| X OAuth 1.0a (Read+Write) | |
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN |
|
| memory | |
GITHUB_USER |
|
| the GitHub username to watch | |
ALLOWLIST |
|
comma-separated owner/repo . Leave empty = all your public repos (skips your profile repo and forks). |
|
CRON_SECRET / INTERNAL_SECRET |
|
| endpoint protection (random strings) |
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.
Every post — automatic or manual — runs through lib/safety.ts
before it can reach approval:
Regex backstop— instantly blocks anything matching an API key, token, private key, Stripe/AWS/GitHub secret, or a security-bug disclosure.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
| Item | Per 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.
| Symptom | Fix |
|---|---|
| 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 403 | |
| Set 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_FOUND | |
Vercel Node functions need self-contained imports — keep api/telegram-webhook.ts dependency-light (it is, by design). |
|
| Cron runs but nothing posts | |
| That's correct if you had no post-worthy public activity that day — it never invents posts. | |
The /announce post links the wrong place |
|
| Private projects link to your GitHub profile by design (never the private repo). |
MIT © Othmaro Fallas Rojas — @Othmaro_dev · github.com/othmarodev
Built in public. This bot announces its own updates. 🤖