A Telegram bot that builds small web apps on demand. Describe what you want;
an Anthropic tool-use agent writes the code, sandboxes it with bwrap, and
serves it at a public URL. /why <build_id> then narrates the agent's
behavior in plain English so you (or anyone watching) can see what it did
and why.
Heads up: this is a single-author hobby project, designed for a trusted-whitelist Telegram bot on one Linux box. It is not multi-tenant, not hardened against hostile users on your whitelist, and not packaged for distribution. The sandbox isolates the filesystem but allows network egress (so the proxy can reach the app). Read
apparty/sandbox.pybefore opening it to anyone you don't trust.
/build <description>— agent plans, writes files into a per-build sandbox dir, optionally starts a Python-stdlib backend, and returns a shareable URL (<base-url>/builds/<id>/)./edit <build_id> <change>— resumes the agent on an existing build./builds— lists your recent builds./why <build_id>— Sonnet narrates what the agent did, in plain English.
Each build is capped at $2.00 worth of model spend (configurable). The
20 most recent per user are kept; older ones (and their backend processes)
are auto-deleted.
- Telegram bot (
apparty/bot.py) — polling, whitelist, command handlers. - Build agent (
apparty/build_agent.py) — Anthropic tool-use loop withwrite_file/read_file/list_files/run_command/start_app/donetools. Tracks tokens + cost per build. - Sandbox (
apparty/sandbox.py) —bwrapwrapper.run_commandruns with--unshare-net(no network).start_appshares host network so the proxy can reach the backend./homeand/rootare never bind-mounted, so the bot's.envis invisible to sandboxed code. - Proxy (
apparty/proxy.py) — stdlibThreadingHTTPServeron127.0.0.1:9100. nginx forwards/builds/here. Routes each request to the build's running backend (if any) or serves static files from the build dir. - Narrator (
apparty/narrator.py) — replays the recorded message log through Sonnet to produce a short plain-English story of what happened. - State (
apparty/db.py) — SQLite for users, builds, message log.
You'll need: a Linux box you control, bubblewrap, nginx, Python 3.11+,
a public hostname (for shareable URLs), a Telegram bot token, and an
Anthropic API key.
sudo apt install -y bubblewrap nginx python3-venv
git clone <this repo> ~/repos/apparty
cd ~/repos/apparty
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
cp .env.example .env| Var | What | Where to get it |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Bot token | Message @BotFather, /newbot |
APPARTY_ALLOWED_USERS |
Comma-separated numeric Telegram user IDs | Each user messages @userinfobot and copies their ID |
ANTHROPIC_API_KEY |
API key | console.anthropic.com |
APPARTY_BUILDS_BASE_URL |
Public base URL, e.g. https://apps.example.com |
Your domain |
See NGINX_SETUP.md. There's a ready-to-edit server block at
nginx/apparty.conf.example.
A foreground process inside a terminal will die when the terminal goes away. Use a user-level systemd unit instead:
# ~/.config/systemd/user/apparty.service
[Unit]
Description=Apparty Telegram app-builder bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/repos/apparty
EnvironmentFile=%h/repos/apparty/.env
ExecStart=%h/repos/apparty/.venv/bin/apparty
Restart=on-failure
RestartSec=10
StandardOutput=append:%h/repos/apparty/logs/bot.log
StandardError=append:%h/repos/apparty/logs/bot.log
[Install]
WantedBy=default.targetloginctl enable-linger $USER # survive logout
systemctl --user daemon-reload
systemctl --user enable --now apparty.service
systemctl --user status apparty.service| Var | Default | Notes |
|---|---|---|
APPARTY_BUILD_MODEL |
claude-sonnet-4-6 |
The build agent's model |
APPARTY_BUILD_BUDGET_CENTS |
200 |
Per-build hard cap, in cents |
A typical small build (5–15 files, a few iterations) lands at $0.20–$0.60 with Sonnet 4.6. The $2.00 cap exists to bound surprises — most builds end well under it.
apparty/
├── apparty/
│ ├── bot.py # Telegram entry point + command handlers
│ ├── build_agent.py # Anthropic tool-use loop
│ ├── narrator.py # /why story generator
│ ├── proxy.py # localhost:9100 HTTP proxy
│ ├── sandbox.py # bwrap wrapper
│ ├── db.py # SQLite
│ └── config.py # env loading
├── nginx/
│ └── apparty.conf.example
├── NGINX_SETUP.md
├── .env.example
└── pyproject.toml
builds/, data/, logs/, and .env are gitignored — generated apps and
state live on your box, not in source control.
- Single-user-per-process by design (one active backend per whitelisted user; starting a new build kills the previous backend).
- Backends use Python stdlib only — no
pip installinside the sandbox. - Backends are
bwrap --die-with-parentof the bot. They die when the bot restarts, but the bot auto-revives them on startup by replaying the laststart_appcommand from each build's persisted message log. - No auth on
/builds/<id>/; treat every build URL as public-ish. If you want it private, gate/builds/in nginx with basic auth or Cloudflare Access.