February 21, 2026β’3 min read
I wanted a dead-simple workflow: write a messy βTILβ, and have it show up on my website.
No admin panel. No extra CMS. Just one message and magic πͺ.
Why I changed my workflow
I used to keep TILs in a local file on my laptop.
It worked, but the UX was basically filesystem + editor for writing, and no great visualizer for consuming it later.
That created friction: if I learned something while away from my laptop, I either forgot it or postponed writing it.
Moving capture to Telegram + OpenClaw fixed that. Now I can log ideas anywhere, then let the pipeline curate and publish.
The idea
Iβm using OpenClaw with Telegram integration as the capture layer.
Then I added a curation flow:
- I send a raw
TIL: ...note in Telegram - OpenClaw curates it into a proper entry file
- It updates an
entries/index.json - It creates a branch, commits, pushes, and opens a PR
- After merge, the content is displayed in a website til.yusefhabib.com
Thatβs it. Capture fast, clean up automatically, review in GitHub.
Hosting the TIL app on a VPS
I run the TIL app on a subdomain and front it with Caddy.
sudo apt update
sudo apt install caddy -y
sudo systemctl enable --now caddytil.yourdomain.com {
root * /var/www/til.yourdomain.com
encode gzip zstd
file_server
}Then DNS:
- Type:
A - Host:
til - Value:
<your-vps-ip>
Quick propagation check:
dig +short til.<yourdomain>.comIf it returns your VPS IP, youβre good.
Small deployment detail: symlinks > copy
At first I copied files into /var/www/.... It worked, but every content change required another copy.
So I switched to symlinks:
ln -s /srv/til/docs/index.html /var/www/til.<yourdomain>.com/index.html
ln -s /srv/til/docs/app.js /var/www/til.<yourdomain>.com/app.js
ln -s /srv/til/docs/style.css /var/www/til.<yourdomain>.com/style.css
ln -s /srv/til/entries /var/www/til.<yourdomain>.com/entriesNow source changes are reflected immediately on the live app.
The Telegram β PR workflow
I keep one convention: prefix raw notes with TIL:.
TIL: dig +short <domain> to verify DNS changes and confirm A record is correctOpenClaw turns that into:
entries/YYYY-MM-DDTHHMM_slug.md- a new item in
entries/index.json(newest first) - a branch + commit + PR
Always branch + PR. No direct pushes to main.
How the TIL repository is structured
The repo is intentionally simple:
til/
βββ entries/
β βββ index.json
β βββ YYYY-MM-DDTHHMM_slug.md
βββ docs/
βββ index.html
βββ entry.html
βββ app.js
βββ style.cssHereβs the split of responsibilities:
entries/*.md: one markdown file per learningentries/index.json: metadata used by the UI (title, date, tags, file)docs/: static frontend that reads index.json and renders list/detail pages
This separation keeps authoring and presentation decoupled: I write markdown entries, and the UI automatically picks them up from metadata.
Why this setup is better
It removes friction at capture time.
When I learn something, I dump it quickly in Telegram and keep moving. OpenClaw handles formatting, metadata, and git workflow. I only review/merge.
Fast input, clean output, full history, and content flowing into til.yusefhabib.com.
If you want to replicate it
Start with these rules:
- OpenClaw + Telegram for capture
- static TIL app on
til.<yourdomain>.com - markdown entries +
index.json TIL:prefix for ingestion- branch + PR only
Minimal moving parts, high leverage.
Bonus
My website pulls the latest 5 TILs directly from the repo for my Latest Activity section.
Do you like this setup? Share your thoughts with me @yhabibf