În dezvoltare — încă nu e publicat în magazinele de aplicații
Mesaje pe care nimeni altcineva nu le poate citi.
Invictus este un chat criptat cap la cap. Criptarea se face pe telefon, cu libsignal — același protocol Signal, cu schimb de chei rezistent la calculatoare cuantice. Serverul transportă octeți pe care nu îi poate deschide.
Cont fără număr de telefon și fără adresă de e-mail. Clientul Android e testat pe dispozitive reale; cel de iOS e scris, dar încă nu poate primi mesaje cu aplicația închisă.
Criptare
libsignal, cu chei rezistente la cuantic
Invictus nu are un protocol propriu. Folosește libsignal 0.86.5,
biblioteca lui Signal: PQXDH pentru stabilirea sesiunii — schimb de chei
clasic combinat cu Kyber-1024, ca o înregistrare de trafic de azi să nu
poată fi descifrată mâine de un calculator cuantic — și Double Ratchet
pentru mesaje, deci fiecare mesaj are cheia lui. În grupuri se folosesc
sender keys, rotite la fiecare schimbare de membri.
Cheia de dispozitiv
ECDSA P-256 · în hardwareTrăiește în Android Keystore / StrongBox și nu poate fi exportată. Autentifică telefonul la server. Nu iese niciodată din elementul securizat.
Identitatea Signal
Curve25519Identitatea de mesagerie, cea care intră în sesiunile Double Ratchet.
Cheia de cont
ECDSA P-256 · portabilăSemnează fiecare dispozitiv al tău. Clientul verifică semnătura înainte să deschidă o sesiune — asta e apărarea împotriva unui server care ar inventa un dispozitiv nou pe contul tău.
Identificatori care se verifică singuri
ID-ul de contact INVC-… este un hash SHA-256 al cheii tale
de cont. Doi oameni care compară ID-uri compară chei, nu nume alese de
server. În plus, fiecare conversație are un număr de siguranță de 60 de
cifre, identic pe ambele telefoane.
Registru de chei verificabil
Serverul ține un jurnal Merkle append-only al cheilor publice, cu vârf semnat Ed25519. Clientul verifică semnătura și dovada de incluziune și se asigură că cheia dovedită e chiar cea primită — un server care răspunde cu o cheie și dovedește alta este prins.
Funcții
Ce face aplicația
Totul de mai jos există în clientul Android. Unde o funcție e făcută doar pe jumătate, scrie chiar acolo; restul lucrurilor neterminate sunt strânse în secțiunea de stare, la sfârșit.
Cont fără număr de telefon
Nu ți se cere niciun SMS și nicio adresă de e-mail, iar parolă nu există. Contul e un nume de utilizator ales de tine plus o pereche de chei, iar autentificarea înseamnă semnarea unui nonce trimis de server. Nu există hash de parolă care să se poată scurge.
Mesaje efemere
Un temporizator per conversație, sincronizat între telefoane prin canalul criptat. E pornit din start, cu o săptămână implicit: o funcție de siguranță pe care trebuie să o descoperi și să o pornești nu apără pe nimeni.
Ștergerea scoate rândul din baza de date locală, nu îl ascunde din interfață, și curăță și previzualizarea din lista de conversații. În grupuri temporizatorul e deocamdată local: îl schimbi la tine, nu la ceilalți.
Apeluri criptate
Voce și video peste WebRTC: media e criptată cu DTLS-SRTP, iar negocierea trece tot prin canalul criptat cap la cap — un server care ar putea rescrie amprentele SDP s-ar putea altfel așeza la mijloc.
Pe Android, un serviciu de prim-plan face telefonul să sune și cu ecranul stins. Pe iPhone asta încă nu funcționează.
Mai multe telefoane, un cont
Poți lega un al doilea dispozitiv, tastând un cod scurt de pe telefonul nou pe cel vechi. Legarea cere cheia de cont, nu doar o sesiune activă: un token de acces furat nu poate atașa un dispozitiv străin. Fiecare dispozitiv nou e semnat de cheia de cont, iar ceilalți verifică semnătura.
Deocamdată incomplet: telefonul legat primește mesajele de grup, dar conversațiile 1 la 1 merg tot către un singur dispozitiv, iar istoricul nu se transferă. Sincronizarea completă între dispozitive nu e gata.
Grupuri
Numele grupului e criptat — serverul nu are câmp pentru el. Când cineva e scos, epoca grupului crește și clienții generează o cheie nouă de trimitere; altfel excluderea ar fi cosmetică, fostul membru rămânând cu cheia veche.
Atașamente
Fiecare fișier primește o cheie proprie de 32 de octeți și e criptat AES-256-GCM pe telefon. Serverul primește numai octeți sigilați; cheia călătorește prin canalul criptat.
Cod de acces și amprentă
Datele de pe telefon sunt criptate valoare cu valoare, sub o cheie ținută în Keystore. Cu blocarea aplicației pornită, cheia e învelită sub o cheie Keystore care cere autentificare biometrică la fiecare folosire — nu o fereastră de valabilitate.
O amprentă înrolată de altcineva distruge cheia în loc să deschidă ușa.
Seif — copie de siguranță a cheilor
Cheia de cont poate fi salvată într-un seif sigilat cu o parolă de-a ta: Argon2id (minim impus de server) plus AES-256-GCM. O parolă greșită nu produce text aiurea, ci eșuează la verificarea etichetei GCM.
Seiful păstrează doar cheia contului — atât cât să-ți recapeți identitatea pe un telefon nou. Mesajele și fișierele nu sunt salvate nicăieri; dacă pierzi telefonul, pierzi conversațiile.
Cod de constrângere
Un al doilea cod care, la introducere, distruge cheile din Keystore și golește stocarea locală înainte de a raporta succes. Pe disc, cele două coduri sunt indistinctibile: nimic nu trădează că există un al doilea.
Serverul
Un releu, nu o arhivă
Serverul e un singur binar Go, cu PostgreSQL, fără Docker și fără dependențe la rulare. Nu decide nimic despre conținut, pentru că nu îl poate vedea. Merită spus și ce vede totuși: ascunderea unei părți din metadate e inerentă oricărui sistem care rutează mesaje.
Ce nu poate citi serverul
- Conținutul mesajelor — criptate înainte să plece de pe telefon.
- Atașamentele — încărcate deja sigilate, cu cheie per fișier.
- Numele grupurilor — nu există câmp pentru ele în bază.
- Numele afișat și poza de profil — călătoresc cap la cap, ca orice mesaj.
- Copiile de siguranță ale cheilor — sigilate cu parola ta.
Fără parole
Un cont e o pereche de chei. Nu există nimic de refolosit de pe alt site și niciun hash de parolă care să se scurgă.
Jetoane Ed25519, nu JWT
Formatul fixează algoritmul. Agilitatea JWT — alg:none,
confuzia HS256/RS256 — a produs un șir lung de ocoliri de autentificare.
Revocare imediată
Sesiunea e verificată la fiecare cerere. Delogarea sau revocarea unui dispozitiv invalidează pe loc jetoanele deja emise, nu la expirare.
Jetoane de reîmprospătare rotite
Dacă unul e furat și folosit, următoarea reîmprospătare a clientului legitim eșuează: furtul iese la suprafață în loc să treacă neobservat.
Livrat înseamnă șters
Mesajele livrate se șterg la confirmare; cele nelivrate expiră. Stocarea e mărginită de politică, nu de folosință.
Înregistrare pe bază de invitație
Implicit închisă. Codul se generează pe server și se afișează o singură dată; în bază se păstrează doar hash-ul lui.
Portofel și abonament
Plata fără să treci printr-un nume
Un cont care nu cere număr de telefon n-ar avea niciun rost dacă plata ar cere un card pe numele tău. De aceea abonamentul se plătește în cripto, iar aplicația are un portofel propriu pe telefon.
Portofel self-custodial, pe telefon
Cheile sunt generate pe dispozitiv dintr-o sămânță BIP-39 și derivate BIP-32/BIP-44. Sămânța nu pleacă nicăieri: e ținută criptată sub o cheie din Android Keystore, deblocată cu amprentă la fiecare semnare. Serverul nu o vede și nu o poate recupera. Pe un telefon fără amprentă înrolată protecția scade la codul aplicației, iar ecranul portofelului ți-o spune direct.
Fraza de recuperare e singura copie: nu există nimeni de la care să o ceri înapoi. Asta înseamnă self-custodial.
Poți primi și trimite: adresă cu cod QR, iar la trimitere semnarea se face pe telefon, cu cheia derivată în acel moment și nepăstrată după aceea — fiecare operație cere amprenta din nou. Pe Tron se vede și istoricul mișcărilor; pe Base nu, fiindcă nodurile publice refuză căutările prin istorie — iar o listă goală ar fi părut că n-ai făcut nimic.
Înainte de prima întrebare pusă unui nod public, aplicația îți cere voie o dată și ține minte răspunsul. Nodul acela vede și adresa ta, și adresa ta de internet. Nu întrebăm niciun serviciu de prețuri: cifra în dolari presupune că un stablecoin face un dolar.
Abonament lunar, în stablecoin
Prețul implicit e de 5 $ pe lună, plătit în USDC pe rețeaua Base sau în USDT pe Tron. Conturile noi au o perioadă de probă de șapte zile.
Serverul nu are chei și nu semnează nimic: doar întreabă un nod public cât a ajuns la adresa de depunere a facturii. Chiar dacă nodul l-ar minți, cel mai rău lucru care se poate întâmpla e un abonament dat degeaba — n-are ce lua.
Base USDC Tron USDT Probă 7 zile Preț implicit 5 $ / lună
Un lucru pe care nu vreau să-l ascund: ca să afle dacă o factură a fost plătită, serverul întreabă un nod public al lanțului. Acel furnizor vede adresele de depunere și adresa IP a serverului — exact categoria de metadate pe care restul aplicației le evită. Cu un nod propriu dispare, dar asta e o alegere de operare, nu ceva ce poate rezolva codul.
Cerința de abonament este oprită implicit pe server; un operator o pornește când e pregătit. Când e pornită și abonamentul expiră, poți primi mesaje în continuare, dar nu mai poți trimite.
Portofelul și abonamentul sunt încă două ecrane separate: plata se face copiind adresa facturii în formularul de trimitere. Nu există încă un buton „plătește din portofel”.
Taxa de rețea se plătește în moneda lanțului, nu în stablecoin — ai nevoie de puțin TRX pe Tron sau de puțin ETH pe Base, altfel trimiterea nu pleacă. Aplicația ți-o spune în fereastra de confirmare, împreună cu adresa întreagă a destinatarului.
Limite
Ce nu protejează Invictus
Scris aici ca să nu descopere nimeni pe cont propriu, la un moment prost. O aplicație de securitate care promite mai mult decât livrează e mai periculoasă decât una care nu promite nimic.
| Nu apără de | De ce |
|---|---|
| Capturi de ecran și fotografii | Celălalt telefon are textul în clar. „Șterge la amândoi” există, dar e o cerere, nu o garanție: o aplicație modificată o poate ignora, iar aplicația spune asta chiar acolo unde apeși. |
| Un telefon compromis | Un program rău-intenționat cu drepturile aplicației, pe un telefon deblocat, vede exact ce vede utilizatorul. |
| Furnizorul de găzduire, cât timp serverul rulează | Criptarea discului apără discul oprit. Cheia stă în memorie, iar cine controlează hipervizorul o poate citi. |
| Analiza de trafic | Momentul și mărimea plicurilor sunt vizibile pentru server și pentru oricine urmărește rețeaua. |
| Un telefon confiscat în timp ce aplicația rulează | Legarea de amprentă e per pornire de proces, nu per blocare de ecran. Alternativa ar fi un telefon blocat care nu poate suna la un apel primit — o alegere luată deliberat în cealaltă direcție. |
| Un server care arată un registru de chei diferit fiecărui utilizator | Clientul verifică azi doar că o cheie chiar se află în jurnal. Nu compară vârfurile jurnalului între ei — deci un server care ține două jurnale paralele nu e încă detectat. |
| Un adversar care copiază telefonul înainte să ceară codul | Codul de constrângere șterge datele la introducere. Împotriva unei imagini făcute mai devreme nu ajută nimic, pe niciun telefon. |
Invictus nu a trecut printr-un audit de securitate extern. Nu există certificări și nu există cifre de utilizatori de arătat. Ce există e codul, testele lui și un document de model de securitate scris pentru cineva care vrea să-l atace, nu pentru cineva căruia i se vinde.
Stare
Unde e proiectul acum
Invictus e în dezvoltare. Nu e publicat în magazinele de aplicații și nu are încă o instanță publică la care să te poți înscrie.
- Merge Clientul Android Înregistrare, conversații 1:1 și de grup, livrare în timp real prin WebSocket, atașamente, seif și blocare cu cod, verificarea registrului de chei și numere de siguranță, apeluri voce și video. Testat pe dispozitive reale.
- Merge Serverul Binar Go cu PostgreSQL, migrări incluse, limitare de rată, registru de chei, abonamente. Suita de teste rulează criptografie reală, nu simulacre.
- Parțial Clientul iOS Există și acoperă aceleași funcții: înregistrare, conversații, grupuri, seif, blocare cu Face ID, apeluri. Îi lipsesc însă integrarea cu sistemul pentru apeluri și notificările push, deci un iPhone blocat nu sună și nu primește mesaje cu aplicația închisă. Nu e publicat.
- Parțial Sincronizare între dispozitivele tale Legarea unui al doilea telefon funcționează, dar mesajele 1 la 1 ajung într-un singur loc și istoricul nu se mută. Până se termină, tratează al doilea telefon ca pe unul secundar, nu ca pe o copie.
- Ține de instalare Apeluri între rețele diferite Codul de releu TURN și scriptul de instalare pentru coturn există, iar serverul emite deja credențiale efemere cu nume de utilizator pseudonim. Pe o instanță unde releul nu e configurat, apelurile eșuează în spatele unui NAT prin care STUN singur nu poate trece.
- Urmează Instanță publică Instalarea pe server, certificatul TLS și volumul criptat sunt scrise ca proceduri, dar nu rulează încă pentru public. Nu există un loc unde să-ți faci cont astăzi.
- Urmează Codul va fi publicat Intenția e ca sursa să fie deschisă, ca oricine să poată citi ce face aplicația în loc să ne creadă pe cuvânt. La o aplicație care promite că nimeni nu-ți vede mesajele, asta e singura dovadă care contează cu adevărat. Deocamdată depozitul nu are o licență aleasă, deci nu spunem încă sub ce termeni, și nici când.
- Urmează Supraveghere a serverului cu un model de limbaj Plănuim un model care să urmărească semnalele de operare ale serverului și să dea alarma la tipare de atac: rafale de autentificări eșuate, semnături invalide, înregistrări în valuri, dispozitive adăugate sau revocate anormal, conturi lovite de limitarea de rată. Exact evenimentele care ajung deja în jurnalul de audit, unde adresa IP intră trecută prin hash cu o sare regenerată la fiecare pornire. Ce nu va putea face, și de ce: nu va citi mesajele. Nu fiindcă ne abținem, ci fiindcă pe server nu există text în clar — mesajele sunt criptate pe telefon, iar cheile nu ajung niciodată acolo. Un model care rulează pe server vede exact ce vede serverul: plicuri închise, momentul și mărimea lor. Nu e moderare de conținut și nu poate deveni așa ceva fără să spargem criptarea.
Verificat, dar nu pe hardware: legarea cheii de date de amprentă e implementată și raționată, însă nu a fost încă exercitată pe un telefon fizic — emulatorul nu a putut înrola o amprentă. Restul straturilor de stocare sunt acoperite de teste.