Šepot — end-to-end encrypted messenger
Initial public release. Anonymous on-device identity (no phone number, e-mail, or password), Signal-protocol cryptography (X3DH + PQXDH, Double Ratchet, sealed sender) via libsignal, a metadata-minimizing relay, group chats, disappearing messages, encrypted local storage, optional Tor transport, and a UI localized into Czech, English, German, Russian, and Italian. Monorepo: Flutter client (app/), Rust relay + wire types (rust/), protobuf definitions (proto/), self-host deploy (deploy/). Licensed under AGPL-3.0 with an app-store distribution exception (§7).
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# Zkopíruj jako `.env` vedle docker-compose.yml a vyplň. NIKDY necommitovat.
|
||||
|
||||
# --- Povinné ---
|
||||
# Heslo k PostgreSQL (zvol silné, náhodné).
|
||||
POSTGRES_PASSWORD=
|
||||
|
||||
# --- Volitelné: nativní APNs push (iOS). Nech prázdné, pokud nepoužíváš —
|
||||
# relay pak jede na UnifiedPush. Klíč .p8 se mountuje v docker-compose.yml.
|
||||
SEPOT_APNS_KEY_ID=
|
||||
SEPOT_APNS_TEAM_ID=
|
||||
SEPOT_APNS_TOPIC=
|
||||
@@ -0,0 +1,37 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# sepot-relay — multi-stage build (kontext = kořen monorepa, viz compose)
|
||||
#
|
||||
# Build stage potřebuje: protoc (prost-build generuje z proto/sepot.proto),
|
||||
# git (libsignal-protocol je git dependency s [patch.crates-io] zrcadlem).
|
||||
|
||||
FROM rust:1.96-slim AS build
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
protobuf-compiler git pkg-config build-essential cmake \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /src
|
||||
COPY proto/ proto/
|
||||
COPY rust/ rust/
|
||||
WORKDIR /src/rust
|
||||
# Cache mounty: rebuild po změně zdrojáků nekompiluje závislosti znovu.
|
||||
# Binárka se musí zkopírovat VEN z cache mountu (po RUN kroku zmizí).
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/src/rust/target \
|
||||
cargo build --release -p sepot-relay \
|
||||
&& cp target/release/sepot-relay /sepot-relay
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates curl \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& useradd -r -s /usr/sbin/nologin sepot \
|
||||
# předvytvořit s právy sepot — named volume zdědí ownership z image
|
||||
&& mkdir -p /var/lib/sepot/attachments \
|
||||
&& chown -R sepot /var/lib/sepot
|
||||
COPY --from=build /sepot-relay /usr/local/bin/sepot-relay
|
||||
USER sepot
|
||||
EXPOSE 8470
|
||||
# Migrace (sqlx::migrate!) jsou zakompilované v binárce, běží při startu
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s \
|
||||
CMD curl -fsS http://127.0.0.1:8470/v1/health || exit 1
|
||||
CMD ["sepot-relay"]
|
||||
@@ -0,0 +1,74 @@
|
||||
# sepot-relay — self-hosting
|
||||
|
||||
Relay je „hloupý" doručovací server: přijímá a doručuje opaque sealed-sender
|
||||
obálky, drží offline frontu (TTL) a šifrované bloby příloh. **Nevidí obsah ani
|
||||
odesílatele, neváže identitu na telefon/e-mail a neloguje IP.**
|
||||
|
||||
Stack: `sepot-relay` (Rust/axum) + PostgreSQL + volitelný Tor onion.
|
||||
|
||||
## Rychlý start
|
||||
|
||||
```bash
|
||||
cd deploy/relay
|
||||
cp .env.example .env
|
||||
# vyplň POSTGRES_PASSWORD, např.: openssl rand -base64 24
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
TLS/WSS neterminuje relay sám — postav před něj reverzní proxy (Caddy, Traefik,
|
||||
nginx…) s platným certifikátem, která forwarduje na `sepot-relay:8470`. Uvnitř
|
||||
sítě jede plain WS (proxy dělá TLS termination). Klient se pak připojuje na
|
||||
`wss://<tvoje-doména>`.
|
||||
|
||||
### Ověření
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8470/v1/health # {"status":"ok"}
|
||||
curl -s http://127.0.0.1:8470/v1/server-info # capabilities + trust_root
|
||||
```
|
||||
|
||||
### Tor onion (volitelné)
|
||||
|
||||
Compose obsahuje kontejner `tor`, který relay vystaví jako onion hidden service.
|
||||
Adresu zjistíš po startu:
|
||||
|
||||
```bash
|
||||
docker exec sepot-relay-tor cat /var/lib/tor/sepot/hostname
|
||||
```
|
||||
|
||||
Tuhle `.onion` adresu pak nastav v klientu (`app/lib/core/constants/app_constants.dart`,
|
||||
`AppConstants.relayOnion`) pro svůj vlastní build.
|
||||
|
||||
## Konfigurace (`.env`)
|
||||
|
||||
| Proměnná | Popis |
|
||||
|---|---|
|
||||
| `POSTGRES_PASSWORD` | heslo DB — **povinné**, nikdy necommituj |
|
||||
| `SEPOT_APNS_*` | volitelné, jen pro iOS APNs push (Key ID, Team ID, cesta ke klíči) |
|
||||
| FCM service account | volitelné, jen pro Android FCM „fast mode" (mimo repo) |
|
||||
|
||||
Push je volitelný. Bez něj appka funguje přes UnifiedPush (degooglované) nebo
|
||||
si zprávy stáhne po otevření.
|
||||
|
||||
## Persistentní data a zálohy
|
||||
|
||||
Zálohuj **dvě** volumes, obě obsahují kritické tajné klíče (`chmod 600`):
|
||||
|
||||
- **`db-data`** — kromě dat drží `relay_keys` = **trust root CA** sealed
|
||||
senderu. Klienti si ho pinují. Jeho ztráta / regenerace = všichni klienti
|
||||
tvrdě odmítnou komunikovat („Trust root se změnil").
|
||||
- **`tor-keys`** — onion identita (`hs_ed25519_secret_key`). Ztráta = nová
|
||||
onion adresa pro všechny.
|
||||
|
||||
```bash
|
||||
# DB dump: docker exec sepot-relay-db pg_dump -U sepot sepot_relay | gzip > db.sql.gz
|
||||
# Tor klíče: docker run --rm -v sepot-relay_tor-keys:/k -v "$PWD":/o alpine tar czf /o/tor-keys.tgz -C /k .
|
||||
```
|
||||
|
||||
## Privacy — Definition of Done
|
||||
|
||||
- Relay neloguje obsah, odesílatele ani IP (vynuceno v kódu, ověřeno testem).
|
||||
- Access log reverzní proxy pro relay endpoint drž **vypnutý**.
|
||||
- Rate limity jsou in-memory token buckety — nikdy se nezapisují na disk.
|
||||
- DB nemá IP/PII sloupce; offline fronta má TTL, ACK = hard delete.
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Denní záloha sepot-relay — KRITICKÉ.
|
||||
#
|
||||
# db-data → drží relay_keys = trust root CA sealed senderu. Klienti ho
|
||||
# pinují; ztráta/regenerace = všichni klienti tvrdě odmítnou
|
||||
# relay ("trust root se změnil").
|
||||
# tor-keys → onion identita (hs_ed25519_secret_key). Ztráta = nová onion
|
||||
# adresa pro všechny.
|
||||
#
|
||||
# Obojí obsahuje TAJNÉ klíče → zálohy jen chmod 700, umask 077.
|
||||
# Nasazení cronu: viz README.md „Zálohy".
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
|
||||
BACKUP_DIR=/opt/backups/sepot-relay
|
||||
KEEP_DAYS=14
|
||||
STAMP=$(date +%F_%H%M)
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
chmod 700 "$BACKUP_DIR"
|
||||
|
||||
db_file="$BACKUP_DIR/db_${STAMP}.sql.gz"
|
||||
tor_file="$BACKUP_DIR/tor-keys_${STAMP}.tgz"
|
||||
|
||||
# 1) Postgres dump (relay_keys = trust root CA)
|
||||
docker exec sepot-relay-db pg_dump -U sepot sepot_relay | gzip > "$db_file"
|
||||
|
||||
# 2) Tor onion identita — jen podadresář hidden service (hostname +
|
||||
# hs_ed25519_*). Zbytek DataDirectory (cache konsenzu) je regenerovatelný
|
||||
# a jen by churnoval. Read-only mount, ať se běžícímu toru nic nerozbije.
|
||||
docker run --rm -v sepot-relay_tor-keys:/k:ro -v "$BACKUP_DIR":/b alpine \
|
||||
tar czf "/b/tor-keys_${STAMP}.tgz" -C /k sepot
|
||||
# docker run vytvoří soubor s umaskou kontejneru (ne skriptu) → obsahuje
|
||||
# TAJNÝ onion klíč, natvrdo zamknout
|
||||
chmod 600 "$tor_file"
|
||||
|
||||
# 3) Integrita — prázdný/rozbitý dump je horší než žádný (maskuje selhání)
|
||||
if [ ! -s "$db_file" ] || ! gzip -t "$db_file" 2>/dev/null; then
|
||||
echo "$(date -Is) CHYBA: DB dump prázdný nebo poškozený ($db_file)" >&2
|
||||
rm -f "$db_file"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -s "$tor_file" ]; then
|
||||
echo "$(date -Is) CHYBA: tor-keys archiv prázdný ($tor_file)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4) Rotace — držet posledních KEEP_DAYS dní
|
||||
find "$BACKUP_DIR" -maxdepth 1 -name 'db_*.sql.gz' -mtime +"$KEEP_DAYS" -delete
|
||||
find "$BACKUP_DIR" -maxdepth 1 -name 'tor-keys_*.tgz' -mtime +"$KEEP_DAYS" -delete
|
||||
|
||||
echo "$(date -Is) OK db=$(du -h "$db_file" | cut -f1) tor=$(du -h "$tor_file" | cut -f1)"
|
||||
@@ -0,0 +1,104 @@
|
||||
# sepot-relay produkční stack: relay + PostgreSQL 16 + Tor onion
|
||||
#
|
||||
# Nasazení: viz README.md. Kontext buildu = kořen monorepa (../..).
|
||||
# Secrets: .env vedle tohoto souboru (POSTGRES_PASSWORD) — NIKDY do gitu.
|
||||
#
|
||||
# Sítě:
|
||||
# backend (internal) — jen relay ↔ db, bez internetu
|
||||
# tornet — tor ↔ relay; tor potřebuje egress do Tor sítě
|
||||
# Relay publikuje 8470 na loopback hostitele — postav před něj vlastní
|
||||
# reverzní proxy (Caddy/Traefik/nginx) s TLS a forwardni na 127.0.0.1:8470.
|
||||
|
||||
name: sepot-relay
|
||||
|
||||
services:
|
||||
sepot-relay:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: deploy/relay/Dockerfile
|
||||
container_name: sepot-relay
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DATABASE_URL: postgres://sepot:${POSTGRES_PASSWORD}@db:5432/sepot_relay
|
||||
SEPOT_BIND: 0.0.0.0:8470
|
||||
SEPOT_ATTACHMENTS_DIR: /var/lib/sepot/attachments
|
||||
# FCM fast mode (opt-in budíčky přes Google) — SA key ŽIJE JEN NA
|
||||
# serveru (fcm-sa.json vedle .env, chmod 600, NIKDY do gitu)
|
||||
SEPOT_FCM_CREDENTIALS: /etc/sepot/fcm-sa.json
|
||||
# APNs (nativní iOS push, bez Googlu) — auth key .p8 + IDs. Klíč
|
||||
# žije JEN na serveru (apns-key.p8 vedle .env). Bez těchto proměnných
|
||||
# zůstane APNs vypnutý.
|
||||
SEPOT_APNS_CREDENTIALS: /etc/sepot/apns-key.p8
|
||||
# Key ID a Team ID nejsou tajné (tajný je jen .p8) → přímo v compose
|
||||
SEPOT_APNS_KEY_ID: ${SEPOT_APNS_KEY_ID}
|
||||
SEPOT_APNS_TEAM_ID: ${SEPOT_APNS_TEAM_ID}
|
||||
SEPOT_APNS_TOPIC: ${SEPOT_APNS_TOPIC}
|
||||
# log bez obsahu/IP je vynucený v kódu; info stačí
|
||||
RUST_LOG: info
|
||||
volumes:
|
||||
# šifrované bloby příloh (TTL 30 d) — NEzálohují se: efemérní data,
|
||||
# klíče k nim mají jen klienti; ztráta = nedoručené přílohy vyprší
|
||||
- attachments-data:/var/lib/sepot/attachments
|
||||
# VOLITELNÉ push mounty — odkomentuj JEN při zapnutém FCM/APNs push,
|
||||
# zároveň s příslušnými SEPOT_FCM_/SEPOT_APNS_ proměnnými výše.
|
||||
# Bez nich compose běží normálně (rychlý start bez push konfigurace);
|
||||
# necháš-li je odkomentované bez existujících souborů, start selže.
|
||||
# - ./fcm-sa.json:/etc/sepot/fcm-sa.json:ro
|
||||
# - ./apns-key.p8:/etc/sepot/apns-key.p8:ro
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
# Publikováno jen na loopback — reverzní proxy s TLS forwardni sem.
|
||||
ports:
|
||||
- "127.0.0.1:8470:8470"
|
||||
networks:
|
||||
- backend
|
||||
- tornet
|
||||
logging: &log
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: sepot-relay-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: sepot_relay
|
||||
POSTGRES_USER: sepot
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
# Drží i relay_keys (trust root CA!) — záloha = pg_dump, viz README
|
||||
- db-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- backend
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U sepot -d sepot_relay"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
logging: *log
|
||||
|
||||
tor:
|
||||
build: ./tor
|
||||
container_name: sepot-relay-tor
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
# onion klíče — ZÁLOHOVAT (ztráta = nová onion adresa)
|
||||
- tor-keys:/var/lib/tor
|
||||
depends_on:
|
||||
- sepot-relay
|
||||
networks:
|
||||
- tornet
|
||||
logging: *log
|
||||
|
||||
networks:
|
||||
backend:
|
||||
internal: true
|
||||
tornet: {}
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
tor-keys:
|
||||
attachments-data:
|
||||
@@ -0,0 +1,10 @@
|
||||
# Tor hidden service pro sepot-relay — vlastní minimální image
|
||||
# (žádný third-party tor image; privacy produkt nemá kam outsourcovat důvěru)
|
||||
FROM alpine:3.22
|
||||
RUN apk add --no-cache tor \
|
||||
&& mkdir -p /var/lib/tor \
|
||||
&& chown -R tor:tor /var/lib/tor \
|
||||
&& chmod 700 /var/lib/tor
|
||||
COPY torrc /etc/tor/torrc
|
||||
USER tor
|
||||
CMD ["tor", "-f", "/etc/tor/torrc"]
|
||||
@@ -0,0 +1,15 @@
|
||||
# Tor onion vstup pro sepot-relay (onion služba je výchozí přístupový bod)
|
||||
#
|
||||
# Klíče hidden service žijí ve volume `tor-keys` — ZÁLOHOVAT, ztráta
|
||||
# klíčů = nová onion adresa pro všechny klienty.
|
||||
DataDirectory /var/lib/tor
|
||||
HiddenServiceDir /var/lib/tor/sepot
|
||||
HiddenServiceVersion 3
|
||||
# port 80 uvnitř onion → relay v docker síti; onion vrstva šifruje,
|
||||
# TLS uvnitř není potřeba (standardní praxe)
|
||||
HiddenServicePort 80 sepot-relay:8470
|
||||
|
||||
# Jen hidden service, žádný SOCKS proxy ven
|
||||
SocksPort 0
|
||||
# Log bez klientských dat; notice stačí
|
||||
Log notice stderr
|
||||
Reference in New Issue
Block a user