Skip to content

Repository files navigation

bash.social

A browser-based terminal that works as a messenger. No mouse, no buttons: registration, friends, private and group chats, settings — everything is done with commands typed into a command line. Messages arrive in real time over WebSocket, and messages are encrypted at rest.

Stack: Python 3.12 · FastAPI · SQLAlchemy 2 (async) · PostgreSQL · Redis · Docker.

Detailed developer documentation: ABOUT_PROJECT_ENG.md. Русское описание проекта: README_RU.md.

What's inside

  • Two-step login: /login <nick>/password <password>.
  • Friend requests with confirmation: /adduser/accept / /reject.
  • Private and group chats with live notifications and unread counters.
  • Message encryption at rest (Fernet), Argon2 password hashing.
  • Rate limits, security headers, WebSocket origin checks.
  • 17 terminal themes with CRT effects.
  • No telemetry, no external requests, no cookies.

Roadmap

Legend: [x] done · [ ] planned. Checkmarks are updated as the project grows.

Messenger core

  • Registration, two-step login, sessions in Redis.
  • Private chats with live notifications and unread counters.
  • Groups: create, join, invite, kick, rename, leave, delete.
  • Friend requests with /accept / /reject.
  • Settings (profile, theme, /language), security view, /delete_account.
  • /clear_chat wipes a chat history while the chat keeps working.
  • Blocklist with confirmation: /block (via /y) and /unblock.
  • notifications setting: live message notifications on/off, throttled to one per sender every 5 minutes.
  • Random greeting on login and online status in the friend list.
  • 37 automated tests passing (make test) + 77 E2E scenarios against the live HTTP/WebSocket stack (every command covered: auth, friends, chats, groups, blocks, settings, system).

Infrastructure & quality

  • Alembic migrations instead of create_all.
  • CI pipeline: pytest on GitHub Actions.
  • E2E test for the launcher (start.sh / start.bat / make start) on all three OS.

Next features

  • Message editing and deletion (with notifications).
  • Typing indicator and read receipts.
  • Group admin transfer.
  • Client-side localization driven by /language.
  • Full-text search across chats (/search).
  • Multi-process scaling via Redis Pub/Sub (horizontal).

Requirements

Only one thing matters: either Docker (with Docker Compose), or Python 3.11+ with Redis installed. PostgreSQL is optional — SQLite works fine locally.

The project always runs as three separate services (that is normal):

Service What it is
app the terminal itself — a FastAPI (uvicorn) process
db PostgreSQL database (SQLite is fine for local development)
redis a separate server for sessions, rate limits, presence

Start with one command

The launcher picks the best path automatically: if Docker Compose works, it starts all three services in containers; otherwise it runs natively (starts Redis, creates .venv, installs deps, runs uvicorn — with SQLite if you have no PostgreSQL).

OS One command
macOS make start or ./start.sh
Linux ./start.sh or make start
Windows start.bat (double-click or start.bat in cmd)

Example (macOS / Linux):

git clone https://github.com/AnonimPython/bash.social.git bash.social
cd bash.social
make start

What make start does: creates .env from .env.example if missing, then delegates to ./start.sh. Without Docker it falls back to the native path and shows a hint about SQLite. Windows users just run start.bat.

Open http://localhost:8000 in a browser and register:

/register alice secret123

Docker path (detailed)

Install Docker for your OS:

OS How to install
macOS Docker Desktop (or brew install --cask docker), or OrbStack. Free option: brew install docker docker-compose colima && colima start
Windows Docker Desktop (WSL2 backend). Or winget install Docker.DockerDesktop
Ubuntu/Debian/Kali sudo apt update && sudo apt install -y docker.io docker-compose-v2, then sudo usermod -aG docker $USER (log out and back in)
Arch sudo pacman -S docker docker-compose and sudo systemctl enable --now docker

Verify: docker --version and docker compose version.

Containers and ports:

Container Internal address Host port
app :8000 8000
db db:5432 5433 (5432 on the host is usually taken by a system PostgreSQL)
redis redis:6379 6379

If a port is busy, change it in docker-compose.yml (the left part of "host_port:container_port").

Useful commands:

docker compose ps               # container states
docker compose logs -f app      # follow the app logs
docker compose logs -f db       # database log
docker compose down             # stop (data stays in the pgdata volume)
docker compose down -v          # stop and delete the database volume (all data!)
docker compose up -d --build    # rebuild after code changes

Native install without Docker

Redis (and ideally PostgreSQL) are installed straight into the system. Python 3.11+ is required.

macOS

brew update
brew install python@3.12 redis postgresql@16
brew services start redis
brew services start postgresql@16

createuser -P bs            # enter password bs
createdb -O bs bashsocial

git clone https://github.com/AnonimPython/bash.social.git bash.social
cd bash.social
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Run:

# with PostgreSQL:
BS_DATABASE_URL="postgresql+asyncpg://bs:bs@localhost:5432/bashsocial" make run

# or without PostgreSQL, on SQLite (Redis is still required):
./start.sh

./start.sh detects no PostgreSQL and switches to SQLite automatically.

Ubuntu / Debian / Kali

Kali is a Debian derivative, the commands are the same.

sudo apt update
sudo apt install -y python3 python3-venv python3-pip redis-server postgresql postgresql-client
sudo systemctl enable --now redis-server
sudo systemctl enable --now postgresql

sudo -u postgres createuser -P bs      # enter password bs
sudo -u postgres createdb -O bs bashsocial

git clone https://github.com/AnonimPython/bash.social.git bash.social
cd bash.social
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Run:

BS_DATABASE_URL="postgresql+asyncpg://bs:bs@localhost:5432/bashsocial" make run
# or ./start.sh — it will use SQLite, then PostgreSQL is not needed

Arch Linux

sudo pacman -Syu
sudo pacman -S python python-pip redis postgresql
sudo systemctl enable --now redis

sudo mkdir -p /var/lib/postgres/data
sudo chown -R postgres:postgres /var/lib/postgres/data
sudo -u postgres initdb -D /var/lib/postgres/data
sudo systemctl enable --now postgresql

sudo -u postgres createuser -P bs      # enter password bs
sudo -u postgres createdb -O bs bashsocial

Then the same as above (venv, pip install -e ".[dev]") and run with BS_DATABASE_URL="postgresql+asyncpg://bs:bs@localhost:5432/bashsocial" make run or ./start.sh for SQLite.

Windows (native)

Honestly: Redis is not officially supported on Windows. Options, from most to least convenient:

  • Recommended: WSL2 + Docker (see the Docker path above) — everything just works.
  • Redis inside WSL2: install Ubuntu in WSL2 and follow the Ubuntu section, running the project from there too.
  • Memurai — a Redis-compatible server for Windows.
  • Or run only Redis in Docker and the project natively:
docker run -d --name redis -p 6379:6379 redis:7

Then Python and dependencies (PowerShell):

winget install Python.Python.3.12

git clone https://github.com/AnonimPython/bash.social.git bash.social
cd bash.social
py -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e ".[dev]"

PostgreSQL (if you want it instead of SQLite): install from https://www.postgresql.org/download/windows/ and create the user/database during setup. Or just use SQLite:

$env:BS_DATABASE_URL = "sqlite+aiosqlite:///./dev.db"
$env:BS_REDIS_URL = "redis://localhost:6379/0"
uvicorn app.main:app --port 8000

Simplest on Windows is still Docker Desktop.

Server deployment (Linux + systemd)

For a VPS: Redis and PostgreSQL are system services, the application is a systemd unit. Example for Ubuntu/Debian/Kali (on Arch replace apt with pacman and adjust service names).

sudo apt update
sudo apt install -y git python3 python3-venv redis-server postgresql postgresql-client
sudo systemctl enable --now redis-server
sudo systemctl enable --now postgresql

sudo -u postgres createuser -P bs
sudo -u postgres createdb -O bs bashsocial

sudo git clone https://github.com/AnonimPython/bash.social.git /opt/bash.social
cd /opt/bash.social
sudo python3 -m venv .venv
sudo .venv/bin/pip install -e ".[dev]"

Create .env with real production values (your own DB password and encryption key are mandatory):

cd /opt/bash.social
sudo cp .env.example .env
sudo chown -R $USER .venv .env   # so you do not need sudo to run
# edit .env: BS_DATABASE_URL, BS_ENCRYPTION_KEY, BS_ALLOWED_ORIGINS

Create /etc/systemd/system/bashsocial.service:

[Unit]
Description=bash.social messenger terminal
After=network.target redis-server.service postgresql.service

[Service]
WorkingDirectory=/opt/bash.social
EnvironmentFile=/opt/bash.social/.env
ExecStart=/opt/bash.social/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000
Restart=always
RestartSec=3
User=www-data
Group=www-data

[Install]
WantedBy=multi-user.target

Start and enable it:

sudo chown -R www-data:www-data /opt/bash.social
sudo systemctl daemon-reload
sudo systemctl enable --now bashsocial
sudo systemctl status bashsocial

HTTPS (recommended)

The app already enables HSTS when the request comes over HTTPS, so put a TLS reverse proxy in front of it. The simplest is Caddy:

sudo apt install -y caddy

File /etc/caddy/Caddyfile:

chat.example.com {
    reverse_proxy 127.0.0.1:8000
}

Caddy obtains the TLS certificate automatically. Do not forget to set BS_ALLOWED_ORIGINS=chat.example.com in .env so WebSocket connections pass the Origin check.

Common problems

Symptom Cause and solution
port 8000 already in use Stop the other process or run on another port: uvicorn app.main:app --port 8001 / change "8000:8000" in compose
port 5432 in use (native) A PostgreSQL already runs on the host. Use it (create a database) or change the port in BS_DATABASE_URL
Docker: the db container does not start Host port 5433 is busy — change the left part of "5433:5432" in compose
ModuleNotFoundError: cryptography Dependencies are not installed: pip install -e ".[dev]"
Messages cannot be decrypted after changing the key The encryption key cannot be changed once data exists. Set BS_ENCRYPTION_KEY BEFORE sending the first messages
WebSocket fails on a custom domain Set BS_ALLOWED_ORIGINS=your-domain in .env
No Redis connection Check that redis runs and the URL is correct (redis-cli pingPONG)
CliError: ... request already pending Intended: a friend request is one-time, see /requests

Production checklist

  • BS_ENCRYPTION_KEY is set (generate: python -c "import secrets; print(secrets.token_urlsafe(48))").
  • A strong PostgreSQL password instead of bs, with an updated BS_DATABASE_URL.
  • BS_ALLOWED_ORIGINS with your domain.
  • HTTPS via a reverse proxy (Caddy/Nginx) — HSTS turns on automatically.
  • make test passes.
  • Backups of the pgdata volume (or database dumps).

Configuration (the .env file)

All settings are read from environment variables with the BS_ prefix (app/config.py). Copy .env.example to .env and change what you need.

The most important ones:

Variable Default What it does
BS_DATABASE_URL postgresql+asyncpg://bs:bs@db:5432/bashsocial database connection (asyncpg for PostgreSQL, or sqlite+aiosqlite:///./dev.db)
BS_REDIS_URL redis://redis:6379/0 Redis connection
BS_ENCRYPTION_KEY empty (dev fallback) must be set in production — secret for message encryption; generate with python -c "import secrets; print(secrets.token_urlsafe(48))"
BS_ALLOWED_ORIGINS empty (same host only) comma-separated allowed WebSocket origins
BS_MAX_SESSIONS 5 max active sessions per user
BS_SESSION_TTL 2592000 (30 days) session lifetime in seconds
BS_DEFAULT_LANG ru default UI language for new accounts and guests (ru or en); each user can override with /language
BS_DEFAULT_THEME kali default theme for new accounts (any name from /theme ls)
BS_REGISTER_ENABLED true set to false to disable new registrations (/register then says registration is disabled)
BS_NICK_MIN / BS_NICK_MAX 5 / 15 nick length bounds
BS_PASSWORD_MIN / BS_PASSWORD_MAX 8 / 128 password length bounds
BS_GROUP_NAME_MAX 40 max group name length

Rate limits, input limits and pagination are configured the same way (BS_REGISTER_RATE, BS_MSG_RATE, BS_MAX_INPUT, BS_PAGE_SIZE and so on). The full list with comments is in .env.example.

First steps in the terminal

/register alice secret123   → create an account (auto-login)
/adduser bob                → send a friend request
/requests                   → check requests · accept: /accept bob
/contact bob                → open a private chat
hello!                      → plain text is a message in the active chat
/group create team          → create a group
/theme matrix               → switch the terminal theme
/help                       → list all commands

Every command has a built-in hint: /command help.

Commands (full reference)

The full list of all commands (29), grouped the same way as /help. Aliases are shorter synonyms — both spellings work.

Account

Command Aliases What it does
/register <nick> <password> Creates an account and logs in immediately.
/login <nick> Starts login; the system then asks for the password.
/password <password> Enters the password after /login. Never shown in command history.
/logout Ends the session. Not available inside a chat — /leave first.
/whoami Shows info about your account.
/whois <nick> whoisuser Public info about another user.

Search & friends

Command Aliases What it does
/adduser <nick> Sends a friend request; you become friends once it is accepted.
/accept <nick> Accepts an incoming friend request.
/reject <nick> deny Declines an incoming friend request.
/requests req Shows incoming and outgoing friend requests.
/deluser <nick> Removes a user from your friend list.
/find <fragment> Searches users by a nick fragment (at least 2 chars).
/ls <section> list Universal list: user · my_users · groups · unread.

Messages

Command Aliases What it does
/contact <nick> open Opens a private chat and shows its history.
/msg <text> Sends a message in the current chat. Plain text without /msg also works.
/leave Leaves the current chat — private or group.
/clear_chat wipe Deletes all messages of the current chat; the chat itself stays open.

Groups

Command Aliases What it does
/mk_group <name> Creates a group and opens its chat at once.
/group create <name> Creates a group and prints its invite code.
/group join <code> Joins a group by invite code.
/group invite <nick> Invites a friend (creator only).
/group members Members of the current group.
/group kick <nick> Removes a member (creator/admin).
/group rename <name> Renames the group (creator only).
/group leave Leaves the current group.
/group delete Deletes the group with all its history (creator).
/group <code> Opens a group by its code.

Security

Command Aliases What it does
/delete_account Permanently deletes the account (confirmation /y or /n).
/block <nick> ban Blocks a user (confirmation /y). Can be done from a chat.
/unblock <nick> unban Unblocks a user. Can be done from a chat.
/security sec Account security state: encryption, sessions, language.

Settings & theme

Command Aliases What it does
/settings Shows all settings.
/settings set <key> <value> Sets allow_messages · allow_invites · lang · notifications.
/settings security Security details for your account.
/theme [theme] Switches the terminal theme; /theme ls lists all themes.
/language [language] lang Changes the interface language; without an argument lists languages.

System

Command Aliases What it does
/help [command] Lists all commands, or shows help for one command.
/clear cls Clears the terminal screen.
/history Shows the last entered commands (up to 100).

Flow helpers: /y / /n confirm or cancel a pending action (/block, /delete_account). Plain text (a line without /) is sent as a message into the active chat. Run /help <command> for details on any command.

Security highlights

  • Passwords: Argon2 (app/security/crypto.py).
  • Sessions: opaque tokens in Redis with TTL and a per-user cap.
  • A password must not contain the nick.
  • Rate limits on registration, login, password, commands, messages and WebSocket connections.
  • Security headers on every response: CSP, X-Frame-Options: DENY, Referrer-Policy and others.
  • Chat text is sanitized; message history stores no secrets or chat content.

Tests

make test        # = python -m pytest -q

Every push and pull request is checked by GitHub Actions (.github/workflows/ci.yml): it runs the full suite on Python 3.11 and 3.12 and validates the JS client syntax with node --check. Tests run in-memory (SQLite + fake Redis), so no services are needed for CI. Add a badge to your fork's repo page after pushing: ![CI](https://github.com/AnonimPython/bash.social/actions/workflows/ci.yml/badge.svg)

Project layout

app/
  api/       HTTP and WebSocket entry points
  cli/       parser, command registry, dispatcher, command handlers
  services/  business logic: auth, sessions, users, friendship, chat, groups, settings
  security/  crypto, HTTP headers, sanitization, WebSocket guards
  models/    SQLAlchemy: User, Friendship, Group, GroupMember, Message, Block
  static/    index.html, css, js (the terminal client)
  tests/     automated tests
  config.py  settings (BS_ prefix)

A full file-by-file walkthrough is in ABOUT_PROJECT_ENG.md / ABOUT_PROJECT_RU.md.

About

Messanger like bash terminal

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages