Self-hosted chat for a private local network. A Raspberry Pi runs the server and the web portal; the same portal and ESP32-based devices (starting with the M5Cardputer) connect to it as chat clients over HTTP and WebSocket. Everything lives on your own hardware — no cloud services, no external accounts.
- Features
- Hardware clients
- Install (Raspberry Pi)
- Management
- On-device layout
- Repository layout
- Development
- Releases
- Documentation
- Status
- License
v0.1.0 is the first public release and ships the server side of the product.
Accounts and access
- Registration in open mode, or by application when the server runs in closed mode (admins review the queue).
- Login with session tokens; guest access is available only for web clients.
- Roles: user / moderator / admin, with per-user restrictions and a device blacklist enforced at sign-in and registration.
Chat
- Direct chats and private rooms (optional 4-digit room code to join).
- Public and private groups/channels: private ones are invite-only (invite links
or approved join requests), public ones are discoverable by
@username. - Chat settings for owners: title, description, avatar, visibility, slow mode, history visibility, join-request queue.
- Mentions with @-autocomplete and tap-to-open profiles.
- Responsive UI: phone-first single column, two-pane layout (chat list + chat) on tablets/desktop from 940px, compact chrome for landscape phones.
- Custom chat limits: up to 5 per user, unlimited for admins; guests cannot create chats; admins can access all chats.
- Message history, media attachments, chat avatars.
- Realtime delivery over WebSocket (
/realtime/chat/{chat_id}); REST endpoints for history, sending and moderation.
Admin panel (/admin)
- User management: ban / unban, temporary block, device blacklist, delete.
- Content: applications queue, support queue, blog publishing.
- Access-mode switch (open / closed registration).
- Reports, warnings and moderation tools.
- Chat-only web administration: host, network, service, RFID and hardware controls are deliberately not exposed to browser sessions, including admins.
Interface languages
- The web app and the admin panel support Russian and English. The switcher is on the login screen and in the account / profile page (the admin panel has a RU/EN button in the top bar). The first visit picks the language from the browser locale; the choice is remembered.
Security baseline
- Global and per-route rate limiting.
- Login brute-force guard (per-IP attempt budget with temporary blocks).
- Security events are written to the
audit_log. - Optional physical USB root key: an enrolled stick starts the SSH daemon
when inserted and stops it when removed. Its one-time code rotates on every
insertion, so a copied marker becomes invalid after the next use. Root still
authenticates with a normal OpenSSH public key. See
docs/USB_ROOT_KEY.md.
Ops safety
- Host operations are local-only: use a root TTY/SSH session and
sudo lc-operatorfor service restarts, reboot or shutdown. - The web admin panel has no system, network, hardware or reboot endpoints.
- Application backup/incident records are not a browser-operated host control plane.
Install & management
- One-command installer with an English setup wizard.
- systemd service — start on boot or start manually (your choice in the wizard).
lcmanagement CLI: status, setup, update, backup, logs, keys, uninstall.
Firmware for ESP32-based devices lives in firmware/. The first target is the
M5Cardputer (PlatformIO project with a small screen and keyboard); the
roadmap then adds M5StickC Plus 2, T-Embed CC1101 and Flipper Zero. Clients talk
to the server over HTTP + WebSocket, and the message formats are pinned in
contracts/.
- Raspberry Pi 4/5, 64-bit. Raspberry Pi OS Bookworm or newer (Debian 13 trixie is verified; Python ≥ 3.10 is required).
- Internet access during installation (apt packages + Python wheels).
- Bullseye 64-bit (Python 3.9) is not supported — the installer explains why and suggests upgrading.
curl -fsSL https://github.com/NoTimeToSleep-Team/esp32chat/releases/latest/download/install.sh | sudo bashRun it as any regular user — sudo asks for the password once. The installer:
- downloads the release archive and verifies its sha256;
- checks OS / architecture / Python / disk space and installs missing apt packages (waits out busy apt locks);
- lays out
/opt/local-chat-server, creates a venv and installs dependencies; - generates
config/app.envwith a random session secret, starts the systemd service and waits for/health; - launches the setup wizard.
Install log: /var/log/lc-install.log. Re-running the installer acts as an
update; data/ is never wiped.
The whiptail wizard (English-only in v0.1) walks through:
- administrator account (login + password);
- Wi-Fi access point (on/off, SSID, password, channel, country);
- portal port (default 18080);
- registration mode (open / closed);
- start on boot or start manually;
- optional USB root SSH key (insert a stick with
*.pubkeys, or let the installer create a new key on the stick; its code rotates on each insertion); - summary and apply.
The wizard can be re-run anytime with sudo lc setup; current values are
offered as defaults.
If the USB root key is enabled, the installer asks for the stick after the
server is installed. The stick becomes a physical gate: SSH is open only while
it is inserted, and its one-time code rotates on every insertion. Manage keys
later with sudo lc keys.
# pin a version
LC_VERSION=0.1.0 curl -fsSL https://github.com/NoTimeToSleep-Team/esp32chat/releases/latest/download/install.sh | sudo bash
# skip the wizard (install only)
curl -fsSL https://github.com/NoTimeToSleep-Team/esp32chat/releases/latest/download/install.sh | sudo bash -s -- --no-setup
# wizard answers from a file
curl -fsSL https://github.com/NoTimeToSleep-Team/esp32chat/releases/latest/download/install.sh | sudo bash -s -- --preseed ./preseed.txt
# offline / custom archive
LC_TARBALL_URL=file:///tmp/lc-server-v0.1.0.tar.gz bash install.shAfter install the portal is available at http://<pi-address>:18080.
lc manages the installation (installed as /usr/local/bin/lc):
| Command | What it does |
|---|---|
lc status |
service and portal status |
lc setup |
run the setup wizard (current values as defaults) |
lc start / lc stop |
start / stop the server now |
lc update |
update from the newest release (data/ and app.env are kept) |
lc backup |
archive data/ + app.env |
lc export [FILE] |
bundle data/ + app.env for moving to another server (a .sha256 is written too) |
lc import FILE [--yes] |
restore an export on this server (checks the checksum, keeps a safety backup) |
lc logs |
install log and service journal |
lc keys |
root SSH key manager and USB physical-key gate (list/add/new/remove) |
lc uninstall |
remove the service; data/ is kept (--purge removes everything) |
lc version |
server version |
| Don't mix them with ls Linux command! |
# on the old server
sudo lc export /tmp/lc-export.tar.gz
# copy /tmp/lc-export.tar.gz (+ its .sha256) to the new device,
# install the server there (one command), then:
sudo lc import /tmp/lc-export.tar.gzThe export contains the database, uploads, avatars and settings. lc import
verifies the checksum, exports the current data to /var/backups/ first (when
it is not empty — roll back any time with sudo lc import <that file> --yes),
and restarts the service. The setup wizard can also import an export file
directly during a fresh install ("Start the server with: data from an export
file").
/opt/local-chat-server/ code, venv, data/, config/app.env
/etc/systemd/system/local-chat-server.service
/etc/lc-root-key/ optional USB root-key state (root-only)
/usr/local/bin/lc
/usr/local/sbin/lc-usb-key udev helper for the USB root key
/var/log/lc-install.log
- Service user:
localchat; the portal listens on0.0.0.0:<LCS_PORT>(18080). - Settings live in
config/app.env(mode 600, contains secrets).
| Path | Contents |
|---|---|
server/ |
FastAPI server, web portal, SQLite migrations, installer (setup/), lc CLI, systemd units |
server/app/ |
application code — API routers, services, models, realtime transport, templates/static UI |
firmware/ |
ESP32 client firmware (M5Cardputer first) and the bridge |
contracts/ |
message and sync contracts shared by server and firmware |
tests/ |
server API suites (tests/server) and browser UI checks (tests/ui) |
docs/ |
installation guide, architecture, glossary, device matrix |
scripts/ |
development and deployment helpers |
prompts/ |
prompt sources used to build the project |
Run the server locally:
cd server
python -m pip install -e ".[dev]"
uvicorn app.main:app --reload --no-proxy-headersUseful endpoints:
GET /health,GET /health/ready— liveness / readiness;POST /auth/login,POST /auth/register— auth API;GET /realtime/chat/{chat_id}?session_token=…— WebSocket transport (ping,chat.history,chat.sendin;realtime.connected,chat.message,chat.history,chat.ack,errorout).
Tests: tests/server are Python scripts that talk to a running server
(LC_BASE, LC_ADMIN_PASSWORD env vars point to a device); tests/ui are
browser checks against a live portal (tests/ui/README.md explains the setup).
Build the release tarball:
bash server/scripts/make_release_tarball.sh
# → dist/lc-server-v<version>.tar.gz + .sha256 (server/ only; no data/.venv/__pycache__)Release tags live on main (e.g. v0.1.0). Every release publishes:
install.sh— the standalone bootstrap installer;lc-server-v<version>.tar.gz— the server archive;lc-server-v<version>.tar.gz.sha256— checksum.
lc update on a device pulls the newest release. Older releases stay available
on the releases page.
docs/INSTALL.md— full install guide: requirements, options, wizard, management, uninstall and known limitations.docs/USB_ROOT_KEY.md— physical USB gate for root SSH, key manager and recovery.docs/architecture.md— server architecture and data flows.docs/device-matrix.md— device roles and constraints.docs/glossary.md— project terms.
v0.1.0 is the first public release. The server and web portal are the primary
deliverable; hardware firmware is in early development. Known limitations of
this release (access-point setup on NetworkManager, the admin password briefly
visible in the process list, fresh installs only) are listed at the end of
docs/INSTALL.md.
MIT — see LICENSE.