Š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:
Julius Joska
2026-07-11 17:06:39 +02:00
commit 8081d98c67
309 changed files with 44102 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:sepot/core/network/relay_transport.dart';
import 'package:socks5_proxy/socks_server.dart';
/// Deterministické ověření [TorTransport]: REST i WS musí projít lokálním
/// SOCKS5 serverem k lokálnímu cílovému serveru. Žádná síť ven, žádný
/// libsignal — jen transportní vrstva. Doplňuje živý smoke proti onion
/// (`tor_transport_live_test.dart`, tag `tor`).
void main() {
group('TorTransport přes lokální SOCKS5 proxy', () {
late SocksServer proxy;
late int proxyPort;
late HttpServer target;
late int targetPort;
setUp(() async {
// SOCKS5 proxy, která každý požadavek prostě forwarduje na cíl.
proxy = SocksServer();
proxy.connections.listen((c) async {
try {
await c.forward();
} catch (_) {
// klient zavřel dřív — pro test nepodstatné
}
}).onError((_) {});
final proxySock = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
proxyPort = proxySock.port;
await proxy.addServerSocket(proxySock);
// Cílový server: echo HTTP + echo WebSocket.
target = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
targetPort = target.port;
target.listen((req) async {
if (WebSocketTransformer.isUpgradeRequest(req)) {
final ws = await WebSocketTransformer.upgrade(req);
ws.listen((data) => ws.add(data)); // echo zpět
} else {
req.response
..statusCode = 200
..write('ozvěna:${req.uri.path}');
await req.response.close();
}
});
});
tearDown(() async {
await target.close(force: true);
await proxy.stop();
});
test('REST GET projde přes proxy k cíli', () async {
final t = TorTransport(socksHost: '127.0.0.1', socksPort: proxyPort);
final resp = await t
.httpClient()
.get(Uri.parse('http://127.0.0.1:$targetPort/ahoj'))
.timeout(const Duration(seconds: 10));
expect(resp.statusCode, 200);
expect(resp.body, 'ozvěna:/ahoj');
t.close();
});
test('WebSocket se otevře přes proxy a echuje', () async {
final t = TorTransport(socksHost: '127.0.0.1', socksPort: proxyPort);
final ch = t.connectWs(Uri.parse('ws://127.0.0.1:$targetPort/v1/ws'));
await ch.ready.timeout(const Duration(seconds: 10));
ch.sink.add('ping-přes-tor');
final echo = await ch.stream.first.timeout(const Duration(seconds: 10));
expect(echo, 'ping-přes-tor');
await ch.sink.close();
t.close();
});
test('DirectTransport míří na cíl přímo (kontrolní vzorek)', () async {
final t = DirectTransport();
final resp = await t
.httpClient()
.get(Uri.parse('http://127.0.0.1:$targetPort/primo'))
.timeout(const Duration(seconds: 10));
expect(resp.statusCode, 200);
expect(resp.body, 'ozvěna:/primo');
t.close();
});
});
}