Self-hosted, AI-powered wardrobe management.
Photograph clothes → Gemini categorizes → weather-aware outfit suggestions → Telegram bot + web dashboard.
| AI Categorization | Send a photo; Gemini extracts category, color, brand, material, season, and care instructions automatically |
| Outfit Suggestions | Live weather from Open-Meteo + your actual wardrobe → AI picks outfits suited to the day |
| Virtual Try-On | Upload a reference photo, select items, Gemini generates a try-on image |
| Care Label Scanning | Photograph a tag → AI extracts material and washing instructions |
| Scheduled Notifications | Daily outfit reminders pushed to Telegram on a cron schedule |
| Telegram Chat Agent | Ask anything in plain English — Gemini queries your wardrobe and replies |
| Web Dashboard | Browse, filter, edit items and outfits from a full React UI |
Full walkthrough: youtu.be/9sJEkOxy5HA
Ask about your wardrobe |
Add a clothing item |
Outfits builder |
Virtual try-on — hijab |
Scheduled outfit notification |
Closet page |
AI outfit suggestion with live weather |
Virtual try-on — trench coat |
Transport-agnostic core — the database, AI, and business logic are fully decoupled from the interface layer. Any transport (bot, web, MCP) can be swapped in or out independently.
src/
├── ai/ # Gemini client · categorize · suggest · tryon · scanTag
├── db/ # SQLite + Drizzle ORM (schema, migrations, queries)
├── jobs/ # Croner scheduler — daily outfit push notifications
├── storage/ # Image compression via Sharp
├── tools/ # Core logic: items, outfits, weather
├── weather/ # Open-Meteo fetch + WMO condition mapping
├── combined.ts # Single entry point for production / Render
└── transport/
├── web/ # Express v5 API + React 18 / Vite / Tailwind v4 SPA
└── telegram/ # GrammY bot with Gemini function-calling chat agent
Node.js 20+ — check your version:
node --version # should print v20.x.x or higherIf not installed, download it from nodejs.org (choose the LTS version).
cloudflared — used by npm run dev to create a public HTTPS tunnel automatically:
brew install cloudflare/cloudflare/cloudflared # macOS
# Linux: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/A Gemini API key (free):
- Go to aistudio.google.com/app/apikey
- Sign in with a Google account → click Create API key
- Copy the key — you'll paste it into
.envin a moment
A Telegram bot token:
- Open Telegram and search for @BotFather
- Send
/newbot→ follow the prompts to name your bot - BotFather replies with a token like
7123456789:AAF...— copy it
Your Telegram user ID (so only you can use the bot):
- Search for @userinfobot on Telegram
- Send
/start— it replies with your numeric user ID (e.g.123456789)
git clone https://github.com/Aryan-Jhaveri/VastraBot.git
cd VastraBot
npm install
cd src/transport/web/app && npm install && cd ../../../..cp .env.example .envOpen .env and fill in your values:
| Variable | Where to get it | Required |
|---|---|---|
GEMINI_API_KEY |
aistudio.google.com/app/apikey | Yes |
TELEGRAM_BOT_TOKEN |
@BotFather on Telegram | Yes |
TELEGRAM_ALLOWED_USER_ID |
@userinfobot on Telegram | Yes |
WEB_AUTH_PASSWORD |
Any strong password you choose | Yes |
WEB_APP_URL |
Dev: auto-patched by npm run dev — leave blank. Docker/prod: your permanent tunnel URL |
No for dev |
CLOUDFLARE_TUNNEL_TOKEN |
Docker named-tunnel only — see Step 4 Docker | No |
VISION_MODEL |
Defaults to gemma-4-31b-it. Set to gemini-2.0-flash to use Gemini instead |
No |
One command starts everything — cloudflared tunnel, Express API, Vite dev server, and Telegram bot:
npm run devWhat it does:
- Starts a Cloudflare Quick Tunnel pointing at Vite (
:5173) - Patches
WEB_APP_URLin your.envwith the new tunnel URL automatically - Starts Express on
:3000+ Vite on:5173(with HMR) - Starts the Telegram bot (long polling)
The web dashboard is at http://localhost:5173. The Telegram mini-app button is wired to the tunnel URL automatically — no manual copy-paste needed.
Note:
cloudflaredmust be installed (see Step 1). Quick tunnels get a new random URL on every restart; the bot menu button is updated automatically each time. If you want a permanent URL across restarts, set up a named Cloudflare Tunnel (free, one-time), setWEB_APP_URLto your hostname in.env, andnpm run devwill leave it untouched.
Manual alternative (if you don't want the tunnel):
# Terminal 1
npm run web:dev # Express :3000 + Vite :5173
# Terminal 2
npm run telegram # Telegram bot (WEB_APP_URL optional)If you have Docker Desktop installed:
cp .env.example .env # fill in your values first
docker compose upThe app starts on http://localhost:3000. The Telegram bot starts automatically in the same container. A cloudflared sidecar starts in the background to expose the app publicly.
Tunnel options — edit docker-compose.yml and choose one:
Option A — Quick tunnel (default, no account needed):
- The
cloudflaredservice starts automatically with a random URL - Find the URL in logs:
docker compose logs cloudflared | grep trycloudflare.com - Copy it, set
WEB_APP_URL=<url>in.env, thendocker compose restart closet - URL changes on every
docker compose up
Option B — Named tunnel (permanent URL, recommended for always-on):
- Go to dash.cloudflare.com → Zero Trust → Networks → Tunnels → Create a Tunnel
- Add a Public Hostname:
closet.yourdomain.com→http://closet:3000 - Copy the token shown on screen
- In
.env: setCLOUDFLARE_TUNNEL_TOKEN=<token>andWEB_APP_URL=https://closet.yourdomain.com - In
docker-compose.yml: swap the activecommand:line to the named-tunnel one (instructions in the file) docker compose up -d— URL is permanent, no restarts needed
npm run web:build # build the React SPA once
npm start # starts Express + Telegram bot in one process-
Fork / push this repo to GitHub
-
render.com → New Web Service → connect the repo
-
Set:
- Build Command:
npm install && npm run web:build - Start Command:
npm start
- Build Command:
-
Add environment variables:
Variable Value GEMINI_API_KEYyour key TELEGRAM_BOT_TOKENyour token TELEGRAM_ALLOWED_USER_IDyour Telegram ID WEB_AUTH_PASSWORDstrong password NODE_ENVproduction
Render automatically injects PORT and RENDER_EXTERNAL_URL. The app reads this for the Telegram mini-app button automatically — no WEB_APP_URL env var needed, no tunnel setup, no URL to paste.
Note: Render's free tier has an ephemeral filesystem. For persistent wardrobe data, attach a Render Disk and set
CLOSET_DATA_DIRto the disk's mount path (e.g./data).
VastraBot is a self-hosted app — you clone it, configure it, and run it wherever you want. Here are the most common setups, from simplest to most involved.
Easiest cloud option. Render gives you a free HTTPS URL and handles everything.
- Fork this repo to your GitHub account
- Go to render.com → sign up → New Web Service → connect your fork
- Set build command:
npm install && npm run web:build - Set start command:
npm start - Add your environment variables (GEMINI_API_KEY, TELEGRAM_BOT_TOKEN, etc.) in the Environment tab
- Click Deploy — your app goes live at
https://your-app.onrender.com
Persistent data: Render's free tier has an ephemeral disk — data resets on redeploy. To keep your wardrobe permanently, go to Disks → attach a disk → set mount path
/data→ add env varCLOSET_DATA_DIR=/data.
Similar to Render but more reliable for always-on apps. Stays awake without a paid plan upgrade.
- Go to railway.app → sign up with GitHub
- New Project → Deploy from GitHub repo → select your fork
- Add env vars in the Variables tab
- Railway auto-detects the start command — just deploy
Railway's hobby plan is $5/month and includes 8GB RAM + persistent volumes.
Full control. Runs on any Linux server. Good for always-on with no usage limits.
Popular cheap VPS providers: Hetzner (€4/month), DigitalOcean ($6/month), Vultr ($6/month).
One-time server setup (run these after SSH-ing into your new VPS):
# Install Docker
curl -fsSL https://get.docker.com | sh
# Clone the repo
git clone https://github.com/Aryan-Jhaveri/VastraBot.git
cd VastraBot
# Create your .env
cp .env.example .env
nano .env # fill in your values, then Ctrl+X to saveStart the app:
docker compose up -d # -d runs it in the backgroundYour app is now running on http://your-server-ip:3000.
To get HTTPS (required for Telegram mini-app): point a domain at your server IP, then add Caddy or nginx + Certbot in front.
To update to the latest version:
git pull
docker compose up -d --buildIf you have a spare machine (old laptop, Mac mini, Raspberry Pi 4+), you can run VastraBot locally for free. Accessible on your home network; cloudflared is built into the Docker Compose setup for remote access.
Requirements: Docker installed on the machine.
git clone https://github.com/Aryan-Jhaveri/VastraBot.git
cd VastraBot
cp .env.example .env
# edit .env with your values
docker compose up -dThe web dashboard is at http://[machine-ip]:3000 from any device on your network.
Remote access: The cloudflared sidecar starts automatically. For a stable URL that survives restarts, use Option B (named tunnel) described in Step 4 Docker. Set WEB_APP_URL to your permanent hostname once — the Telegram mini-app button will always point to the right place.
| Render | Railway | VPS | Home server | |
|---|---|---|---|---|
| Cost | Free (with limits) | $5/mo | ~$5–6/mo | Free |
| Setup effort | Very low | Very low | Medium | Low |
| Always on | Free tier sleeps | Yes | Yes | While machine is on |
| Persistent data | Needs paid disk | Yes | Yes | Yes |
| HTTPS | Automatic | Automatic | Manual | Via tunnel |
| Best for | Trying it out | Long-term cloud | Full control | Privacy / zero cost |
Send /start to see all commands.
| Trigger | Action |
|---|---|
| Send a photo | Gemini categorizes and adds the item to your closet |
| Any text | Chat agent answers — can query wardrobe, suggest outfits, check weather |
/outfit |
Fetches weather → AI suggests outfits from your closet |
/weather |
Current conditions at your saved location |
/worn <id> |
Marks an item as worn today |
/add |
Prompts for a photo to add an item |
/myphoto |
Saves a reference photo for virtual try-on |
/cancel |
Exits any active flow |
Virtual try-on is available on the web dashboard, not the bot.
| Page | Description |
|---|---|
| Home | Weather card + AI outfit suggestions (manual trigger, cached per session) |
| Closet | Browse and filter all items; edit details, scan care labels |
| Outfits | Saved outfits grid; create manually or save from AI suggestions |
| Try On | Upload reference photo → pick items → generate try-on image |
| Jobs | Schedule daily outfit reminders via Telegram (cron) |
| Settings | Location, password, Telegram chat ID |
No email reset — this is fully self-hosted. To clear the password:
sqlite3 ~/.closet/closet.db "DELETE FROM settings WHERE key = 'password';"Restart the app and it will prompt for a new password on first load.
VastraBot is built with privacy and self-hosting in mind. For a detailed security overview and to report vulnerabilities, please see our SECURITY.md.
- Strict User Locking: Only the
TELEGRAM_ALLOWED_USER_IDcan interact with the bot. - Password-Protected Web UI: Access to the React dashboard requires a strong password.
- Local-First: Your wardrobe data, photos, and settings remain on your own server or disk.
| Layer | Technology |
|---|---|
| AI | @google/genai — Gemini Flash (vision + function calling) |
| Database | better-sqlite3 + Drizzle ORM |
| Image processing | sharp |
| Web API | Express v5 |
| Frontend | React 18 + Vite + Tailwind CSS v4 |
| Bot | GrammY + @grammyjs/conversations |
| Scheduler | Croner |
| Testing | Vitest |
| Language | TypeScript (ESM) |
MIT — self-hosted, no third-party data sharing beyond Gemini API calls.