A censorship-free, federated, end-to-end encrypted chat platform.
This is the master (production) branch — it powers frogtalk.app.
Day-to-day work lands first on the dev branch (frogtalk.xyz).
Pre-alpha: expect bugs, breaking changes, and incomplete hardening. Not production-ready.
🌐 frogtalk.app — production hub · 🧪 frogtalk.xyz — live dev node · 📥 Downloads · 🤖 Google Play — open beta · 📚 Node guide · 🔌 API reference · 🛡️ Security
You are reading the production branch. It powers the public hub at frogtalk.app and is the line operators should deploy.
Production (master) |
Development (dev) |
|
|---|---|---|
| Git branch | master (this tree) |
dev |
| Public URL | frogtalk.app | frogtalk.xyz |
| Client default | client/official-node.json → https://frogtalk.app |
→ https://frogtalk.xyz |
| Board identity | Frog General · @frog-general |
🔧 Development & support · @frogtalk-support |
| Federation directory | Hub (FROGTALK_FEDERATION_DIRECTORY_HUB=1 on Main) |
Published from frogtalk.app |
| Stability | Slightly more stable; still pre-alpha | May break without notice |
Clone (production):
git clone https://github.com/deadinternetfox/frogtalk.git
cd frogtalkContributing: open PRs into dev, not master. After review and testing on .xyz, maintainers promote to master for .app deploys. See CONTRIBUTING.md.
FrogTalk is a self-hostable, federated chat and social node — a Lilypad (what we call a FrogTalk node): FastAPI + SQLite on the server, vanilla JS in the browser, optional Frog Channel imageboard, Discord/Telegram bridges, WebRTC calls, and Ed25519-signed federation between independent operators. Clients encrypt DMs (Signal Protocol) and private channels (AES-GCM) before data reaches your disk.
- Use the production hub (this branch): frogtalk.app/app — pre-alpha, no uptime or data guarantees
- Try the dev instance: frogtalk.xyz/app — bleeding edge, may reset or break
- Run your own: install under
/opt/frogtalk, complete the CLI setup wizard, join the mesh — full VPS guide · web guide
| Branch | Default client node | Live host | Role |
|---|---|---|---|
master |
frogtalk.app |
frogtalk.app | Pre-alpha production hub — official directory, stable line for operators |
dev |
frogtalk.xyz |
frogtalk.xyz | Active development — features land here first; deploys to the dev server; may break without notice |
| Tor (any branch) | varies | .onion |
Hidden service; FROGTALK_HOME_PAGE=tor — vanity search via node/scripts/tor_vanity_onion.sh |
Workflow: fork and PR into dev. After testing, maintainers merge dev → master for production deploys. The only intentional code diff between branches in git is usually client/official-node.json (see client/README.md); dev also carries a contributor-focused root README.
Official directory: https://frogtalk.app/api/network/servers (hub is always frogtalk.app, not .xyz).
Your chat, your server, your keys. No company in the middle, no plaintext on disk, no telemetry tax.
-
Censorship resistance — more independent Lilypads (nodes) means no single kill switch
-
Policy control — moderation, federation peers, and bridges on your terms
-
Privacy — you operate the infrastructure users connect to (still E2E for DMs/private rooms)
-
Federation — your users can talk to people on other nodes in the same mesh
-
🔐 Real E2E — Signal Protocol (X3DH + Double Ratchet) for DMs, per-room AES-256-GCM (AAD-bound, with key rotation on ban/kick) for private channels. The server stores ciphertext and nothing else.
-
🌐 Federated — your node talks to other nodes; users, profiles, posts, rooms and DMs replicate across the swamp.
-
🧅 Tor-native — flip a flag and your node lives behind a
.onion; clearnet IP never leaks. -
📱 Everywhere — Web, Android (APK + Google Play open beta), iOS (TestFlight), Windows portable, Linux AppImage /
.deb, and Electron desktop. -
🎵 More than chat — DMs, group calls (WebRTC), reels, friend wall, music rooms (YT/Spotify/SoundCloud), Frog Channel imageboard, GIF picker, custom emojis.
-
⚒️ Full API — REST + WebSocket for bots, bridges and custom clients. Discord and Telegram bridges ship in-tree.
| 🔐 E2E Encryption | Signal Protocol for DMs (X3DH + Double Ratchet) and per-room AES-256-GCM (AAD-bound v2 wire format, automatic key rotation on ban/kick) for private channels, client-side only — the server never sees plaintext |
| 🌐 Federated | Your node joins the global FrogTalk directory and talks to other nodes |
| 🔁 Cross-node Sync | Home-signed account import when traveling: profile, themes, client prefs, joined channel settings (slowmode, forwarding lock, themes), DM thread prefs, friends/following, FrogSocial posts — see SECURITY_MODEL.md |
| ⚡ Real-time | WebSocket messaging with auto-reconnect, typing indicators, reactions |
| 🔒 Private Rooms | Passphrase-protected rooms — only members with the passphrase can decrypt |
| 💬 Direct Messages | Fully encrypted DMs between any two users |
| 🖼️ Frog Channel Imageboard | Anonymous 4chan-style thread board with replies, likes, image/video/audio posts, greentext, live board chat, and moderator approval tools |
| 🎞️ Reels | Vertical short-video feed with hot/new/top sorting, reactions, reposts, and comments |
| 📎 File Sharing | Images, video, and file attachments up to 8 MB |
| 🤖 Discord & Telegram Bridges | Mirror rooms to/from Discord channels or Telegram chats |
| 🔔 Push Notifications | Web push for mobile and desktop |
| 🛡️ Admin Dashboard | Moderation tools, live server stats, user management |
| 🔞 18+ Content Warnings | Optional moderator labels on public channels; session age gate before history (not message scanning) |
| 🧩 Bot API | Full REST + WebSocket API for building bots and integrations |
| 🧅 Tor / Onion Routing | Nodes can advertise a .onion address; federation traffic and client connections route through Tor when onion mode is enabled. Onion handoff links target /app, and clearnet address is never leaked for onion-only nodes |
Pre-alpha builds — for testing only. Expect crashes, breaking API changes, and incomplete security review. Do not rely on these for sensitive communications until we leave pre-alpha.
| Platform | Latest | Notes |
|---|---|---|
| 🌐 Web | Open in browser | No install needed |
| 🤖 Android | Latest APK · Releases | v1.6.44-alpha (250) — sideload; group calls, video, screen share |
| 🤖 Android (Google Play) | Open testing (beta) | Join the open beta on Google Play — no sideloading |
| 🐳 Run a Lilypad (Docker) | docker compose up -d |
Self-host the backend — see Self-Host |
| 🐧 Linux AppImage | Latest AppImage | chmod +x then run |
| 📦 Linux .deb | Latest .deb | sudo dpkg -i <downloaded_file>.deb |
| 🪟 Windows (Portable .exe) | Latest portable .exe | Portable single-file — just run |
| 🪟 Windows (.zip) | Latest .zip | Unzip, then run FrogTalk.exe |
| 🍎 macOS | Open in browser | Native macOS build not published yet |
Pick one way to run FrogTalk on Linux — all clients talk to a node (default
frogtalk.app; change server in Settings → Network).
# AppImage — no install, any distro
curl -fsSL -o FrogTalk.AppImage "$(curl -fsSL https://api.github.com/repos/deadinternetfox/frogtalk/releases/latest \
| grep -o 'https://[^"]*AppImage' | head -1)"
chmod +x FrogTalk.AppImage
./FrogTalk.AppImage
# .deb — Debian / Ubuntu / Mint
curl -fsSL -o frogtalk.deb "$(curl -fsSL https://api.github.com/repos/deadinternetfox/frogtalk/releases/latest \
| grep -o 'https://[^"]*_amd64.deb' | head -1)"
sudo dpkg -i frogtalk.deb
frogtalkOr use the buttons on GitHub Releases or frogtalk.app/download.
git clone https://github.com/deadinternetfox/frogtalk.git
cd frogtalk
bash client/desktop/scripts/build-linux-release.sh
# Artifacts: client/desktop/builds/*.AppImage and *.debDetails: client/README.md.
Open frogtalk.app/app — no install; works on Linux mobile and desktop browsers.
Docs: docs/NODE_INSTALL.md (VPS, DNS, firewall, nginx, HTTPS, backups) · https://frogtalk.app/docs/node (same flow on the live site)
cd /opt
git clone https://github.com/deadinternetfox/frogtalk.git
cd frogtalk
# CLI install wizard (venv, .env, symlinks) — not a browser UI
export PUBLIC_URL="https://chat.yourdomain.com"
bash node/scripts/install.sh setup -y --public-url "$PUBLIC_URL"
bash node/scripts/install.sh federation -y --public-url "$PUBLIC_URL"
sudo bash node/scripts/install.sh systemd -yOr use the interactive menu: bash node/scripts/install.sh → setup · federation · systemd · status. See docs/NODE_INSTALL.md for a full copy-paste VPS guide.
Put nginx + certbot in front (ports 80/443), keep uvicorn on 127.0.0.1:8080, set
PUBLIC_URL=https://chat.yourdomain.com and matching ALLOWED_ORIGINS. See the VPS guide for UFW,
SSH keys, and troubleshooting.
Admin account: user admin on first boot — password from ADMIN_PASSWORD in .env, or a
one-time generated value in journalctl if left empty (rotate after login).
Runtime state lives at
/opt/frogtalk/(.env,data/,secrets/,venv/). Code is/opt/frogtalk/node/.node/dataandnode/.envmust be symlinks, not real folders.
| Entry | What it does |
|---|---|
bash node/scripts/install.sh |
Interactive menu |
bash node/scripts/install.sh setup |
node_setup_wizard.sh — venv, .env, symlinks |
bash node/scripts/install.sh federation |
Directory sync, hub announce, pubkey pin, board peer pills |
bash node/scripts/install.sh systemd |
Install frogtalk.service |
bash node/scripts/install.sh status |
/api/ping + federation peer list |
Wizard source: node/scripts/node_setup_wizard.sh. Users register at /app after the node is up.
bash node/scripts/node_federation_join.sh --install-dir /opt/frogtalk -y \
--public-url https://chat.yourdomain.com- Directory feed (default):
https://frogtalk.app/api/network/servers - Hub announce: with the same
FROGTALK_FEDERATION_TOKENon Main and your node, federation join POSTs to…/servers/registerso you appear on frogtalk.app (verify with curl to Main, not only your local/api/network/servers) - Peer Ed25519 keys are pinned from each peer’s
/api/network/status - Re-run after changing
PUBLIC_URL, onion URL, or major upgrades
Official mesh nodes (directory + optional fallback_peers in your mesh JSON when the directory HTTP fetch fails):
| Node | Clearnet | Notes |
|---|---|---|
| FrogTalk Main | https://frogtalk.app |
Production hub · board @frog-general |
| FrogTalk Dev | dev branch |
Development instance · board @frogtalk-support |
| FrogTalk Tor Mirror | .onion only |
Listed in Settings → Network when configured |
Federation is not hardcoded in application source. Hub operators copy
node/deploy/federation-mesh.example.json →
federation-mesh.local.json and set FROGTALK_FEDERATION_MESH_FILE. The public FrogTalk fleet
layout is documented as an example only:
federation-mesh.frogtalk.example.json.
See node/deploy/README.md.
git clone https://github.com/deadinternetfox/frogtalk.git /opt/frogtalk
cd /opt/frogtalk
python3 -m venv venv && source venv/bin/activate
pip install -r node/requirements.txt
cp node/deploy/env.example .env
mkdir -p data secrets
ln -sfn /opt/frogtalk/data node/data && ln -sfn /opt/frogtalk/.env node/.env
cd node && python main.pybash node/scripts/install.sh update
bash node/scripts/install.sh update-apply -y
bash node/scripts/install.sh federation -y # refresh peers after releases- Use SSH keys; do not commit
.env, tokens, or passwords to git FROGTALK_FEDERATION_REQUIRE_SIGS=1(default in wizard) — reject unsigned federation eventsFROGTALK_AUTO_UPDATE_ENABLED=0untilFROGTALK_RELEASE_SIGNERSis set- Expose nginx on 443, not raw uvicorn on the public internet
- Report issues: frogtalk.app/security
| Problem | Fix |
|---|---|
| Empty DB / missing tables | node/data must symlink to /opt/frogtalk/data — run setup or federation join |
Domain 502, local :8080 OK |
Match PORT in .env with nginx proxy_pass; behind Cloudflare tunnel use PORT=8000 + FROGTALK_NGINX_TUNNEL_LISTEN=1 (see deploy README) |
/board/ 404 on clearnet |
Route tunnel through nginx on 8080, not uvicorn directly — install.sh board-nginx |
| Wrong board pill URLs | Optional FROGTALK_BOARD_PEER_CANONICAL_FILE — see board-peer-canonical.example.json |
| Federation peers, no delivery | Re-run federation -y; check pinned pubkey in DB (install.sh status) |
| CORS in browser | Add your HTTPS origin to ALLOWED_ORIGINS |
More: docs/NODE_INSTALL.md
Enable the secure node management dashboard:
export FROGTALK_SERVER_WEBUI_ENABLED=1
export FROGTALK_SERVER_WEBUI_USER=serveradmin
export FROGTALK_SERVER_WEBUI_PASSWORD='set-a-strong-password'Then open:
- URL:
https://your-host/server - Login:
FROGTALK_SERVER_WEBUI_USER/FROGTALK_SERVER_WEBUI_PASSWORD
Capabilities include live hardware telemetry (CPU/memory/disk/uptime), federation node inventory, node probe, and block/unblock controls.
The panel now includes a novice-friendly onboarding checklist with explicit warnings for missing HTTPS, public-IP exposure, and recommended federation safety toggles (Tor auto-block and non-SSL peer auto-block).
Node Control also includes a per-node easter-egg editor: upload images/audio/video, format rich text, and set the hidden popup that appears after seven taps on the frog trigger for that node.
Tor / Onion Hidden Service
To run your node as a Tor hidden service (.onion only, no clearnet exposure):
export FROGTALK_TOR_ENABLED=1
export FROGTALK_ONION_URL=http://youronionaddress.onion
# Leave FROGTALK_BASE_URL unset or empty to be onion-onlyUse the onion app surface for user links and server switching:
http://youronionaddress.onion/app
Clients using Prefer onion endpoints in Network Settings will automatically route all federation traffic through Tor. The clearnet IP is never shared with the directory or other nodes when FROGTALK_TOR_ENABLED=1 and no FROGTALK_BASE_URL is set.
Onion-capable nodes display a 🧅 ONION badge in the server list, and the node card shows the .onion address with a one-click copy button instead of a clearnet URL.
sudo cp node/deploy/frogtalk.service /etc/systemd/system/frogtalk.service
# Defaults: WorkingDirectory=/opt/frogtalk/node, EnvironmentFile=/opt/frogtalk/.env
# edit User if you're not deploying as `deploy`
sudo systemctl daemon-reload
sudo systemctl enable --now frogtalk
sudo systemctl status frogtalkLogs: journalctl -u frogtalk -f
The backend node ships as a container. Compose builds it and wires up persistent volumes; front it with nginx or a Cloudflare Tunnel for TLS.
git clone https://github.com/deadinternetfox/frogtalk
cd frogtalk
export PUBLIC_URL="https://chat.<YOUR_DOMAIN>" # once TLS/DNS is set
docker compose up -d
curl -sS http://127.0.0.1:8080/healthz # {"ok":true}Update with git pull && docker compose up -d --build. The image is defined in
node/Dockerfile + docker-compose.yml;
a multi-arch image can be published to ghcr.io/deadinternetfox/frogtalk via the
GHCR workflow. Full node setup (federation, TURN, board, Tor) is in the sections
above and at /docs/run-a-node.
server {
listen 443 ssl;
server_name chat.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Full node setup: docs/NODE_INSTALL.md · frogtalk.app/docs/node
FrogTalk has a full REST + WebSocket API. Build bots, integrations, and custom clients.
GET /api/rooms # list public rooms
POST /api/messages # send a message (with bridge_token)
WS /ws/{room} # real-time message stream
Full reference: frogtalk.app/docs/api — includes
PATCH /api/auth/client-prefs (preferred_node_url, prefer_onion, notification sounds)
for Settings → Network and federation account sync.
FrogTalk's crypto is layered by context so each surface gets the strongest practical guarantee:
- Direct messages — Signal Protocol. X3DH key agreement against the recipient's published prekey bundle establishes a Double Ratchet session. Every DM advances the ratchet, so forward secrecy is per-message and a device compromise tomorrow can't decrypt today's traffic.
- Room messages — per-channel AES-256-GCM with AAD binding + key rotation.
Private channels are sealed with a 256-bit AES-GCM key derived (HKDF-SHA-256)
from a shared channel secret distributed to new members through their
already-established Signal DM session. Ciphertext is bound to a specific
room id and key version via AES-GCM Additional Authenticated Data
(
room:<id>:v<N>, v2 wire format[0x02][iv:12][ct+tag]), so a captured ciphertext cannot be replayed against another room or an older key. When a member is banned or kicked, or a moderator presses Rotate room key now, a fresh key is generated client-side and fanned out to every remaining member via their Signal DM session; the rotation is announced in-channel as a system message. Public channels intentionally have no key — they are designed to be world-readable and are stored encrypted-at-rest only. - Voice/video calls — DTLS fingerprint signing. SDP offers and answers carry an XEdDSA signature over the call's DTLS fingerprint so a hostile signalling server can't silently MITM the media path. A Safety-Numbers panel surfaces the verified peer identity.
- Wall posts — per-post AEAD wrapped to followers. Each post is sealed with a fresh AES-256-GCM key; that key is then wrapped to each follower via their Signal DM session, so only the intended audience can read it.
- Bridged channels. Channels with an outbound Discord/Telegram bridge intentionally fall back to plaintext so the bridge can forward the message text to the third-party platform; this is clearly indicated in the channel header. Bridges are not available for private (E2EE) rooms — forwarding to Discord/Telegram would leak plaintext to a third-party service and defeat end-to-end encryption, so all four bridge-create endpoints reject private rooms with HTTP 403. DMs are never bridged.
- Private keys are generated client-side and never leave the device. They live in IndexedDB (web/desktop) or the OS keystore (Android/iOS).
In-app the 🔒 Encryption info modal exposes the current safety number for a DM, or the channel's encryption mode for a room.
frogtalk/
├── client/ # everything end-users install
│ ├── desktop/ # Electron source + builds
│ │ ├── app/ # Electron source (main.js / preload.js / renderer)
│ │ └── builds/ # Electron output artifacts (gitignored)
│ └── mobile/
│ ├── android/ # Android Studio project (Capacitor + native shell)
│ └── ios/ # iOS Xcode project
├── node/ # the federated server (everything ops cares about)
│ ├── main.py # FastAPI app entrypoint
│ ├── database.py # SQLite schema + migrations
│ ├── routers/ # FastAPI route modules
│ ├── static/ # web client + marketing pages served by the node
│ ├── board/ # Frog Channel PHP imageboard → public /board/
│ ├── deploy/ # systemd / nginx / env.example
│ ├── scripts/
│ │ ├── install.sh # unified installer menu (recommended)
│ │ ├── node_setup_wizard.sh # guided self-host setup
│ │ ├── node_update_check.sh # safe update check / apply
│ │ ├── deploy.sh # rsync node/ to one host
│ │ ├── build_server_release.sh
│ │ └── migrations/ # one-shot historical migrations
│ ├── tests/ # pytest suite (sanitizers, proxy, security)
│ ├── requirements.txt
│ ├── Dockerfile # docker build -f node/Dockerfile -t frogtalk .
│ └── builds/ # release tarballs (gitignored)
├── bot-examples/ # standalone reference bots
├── github-build-mirror/ # release binaries published to GitHub
├── docs/
│ ├── README.md # index of public operator docs
│ ├── NODE_INSTALL.md # VPS install + federation (start here for ops)
│ └── SECURITY_MODEL.md # encryption + threat model
├── README.md / SECURITY.md / CONTRIBUTING.md / CONTRIBUTORS.md / LICENSE
└── .gitignore / .dockerignore / .fallowrc.json
On a running node, operator state (.env, data/, secrets/, venv/) lives at
/opt/frogtalk/ and the runtime source at /opt/frogtalk/node/. The setup wizard
wires symlinks (node/data, node/.env, node/secrets) so the FastAPI process
can stay with cwd=node/ without copying operator secrets into the source tree.
Detailed structure + migration rules: node/README.md · security model: docs/SECURITY_MODEL.md
FrogTalk is MIT-licensed and developed in the open. The encryption primitives are well-studied (X3DH + Double Ratchet for DMs, per-room AES-256-GCM for private channels, DTLS-fingerprint signing for calls). We publish the security model and welcome audits.
- 🐛 Bug or security issue? Report at https://frogtalk.app/security — anonymous submissions accepted. For sensitive disclosures:
[email protected]. - 🛠️ Code contribution? See CONTRIBUTING.md for branch workflow and review expectations.
- 🛠️ Feature idea? File a feature request.
- 📣 Run a Lilypad (node). More Lilypads = more censorship-resistance. Self-host guide above.
- 💬 Spread the word. Community projects need a community.
Researchers who responsibly disclose are credited in the security advisory and on the Hall of Fame.
- Fork the repo and branch from
dev(ormasterifdevis not available yet). - Run
node --check node/static/js/<file>.jsfor any JS you touched. - Run
python3 -m py_compile node/<file>.pyfor any Python you touched. - Open a PR with the template filled in. For security fixes, include a PoC.
- Add yourself to
CONTRIBUTORS.mdin the same PR if you want repo credit.
See /security for scope, threat model, and what counts as a vulnerability.
MIT