{"id":47801234,"url":"https://github.com/tillit-cc/tillit","last_synced_at":"2026-06-27T23:01:06.090Z","repository":{"id":345516059,"uuid":"1170218417","full_name":"tillit-cc/tillit","owner":"tillit-cc","description":"Secure, self-hostable and decentralized communication platform that gives people full ownership of their data and identity. TilliT enables private messaging without relying on central servers, designed for home servers, edge devices and open infrastructure. Built for privacy, transparency and real digital autonomy.","archived":false,"fork":false,"pushed_at":"2026-06-24T15:37:47.000Z","size":661,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-24T17:18:41.785Z","etag":null,"topics":["chat","e2ee","end-to-end-encryption","nestjs","onion","open-source","privacy","raspberry-pi","self-hosted","signal-protocol","tor","typescript","websocket","zero-knowledge"],"latest_commit_sha":null,"homepage":"https://tillit.cc","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tillit-cc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-01T21:38:26.000Z","updated_at":"2026-05-28T16:39:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tillit-cc/tillit","commit_stats":null,"previous_names":["tillit-cc/tillit"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/tillit-cc/tillit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillit-cc%2Ftillit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillit-cc%2Ftillit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillit-cc%2Ftillit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillit-cc%2Ftillit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tillit-cc","download_url":"https://codeload.github.com/tillit-cc/tillit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tillit-cc%2Ftillit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34870654,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat","e2ee","end-to-end-encryption","nestjs","onion","open-source","privacy","raspberry-pi","self-hosted","signal-protocol","tor","typescript","websocket","zero-knowledge"],"created_at":"2026-04-03T17:02:12.171Z","updated_at":"2026-06-27T23:01:06.079Z","avatar_url":"https://github.com/tillit-cc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/tillit-cc/.github/main/profile/logo.png\" alt=\"TilliT\" width=\"120\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eTilliT\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Run your own private messaging server in minutes.\u003cbr\u003e\n  No phone number. No account. Your own hardware.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-AGPL%20v3-blue.svg\" alt=\"License: AGPL-3.0\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003e **Experimental — not production ready.**\n\u003e TilliT is in early development. The core works (encryption, messaging, Tor, media) but expect rough edges, breaking changes, and missing features.\n\u003e **We're looking for early testers** — if you're comfortable with a terminal and want to try a different approach to private messaging, [start here](#quick-start).\n\n## How it works\n\nYou install TilliT on a Raspberry Pi, mini-PC, or any server. Your messages are encrypted on your phone before they leave — the server only relays opaque blobs it cannot read. No central authority, no metadata collection.\n\n```\nPhone A ── E2E encrypted ──\u003e Your TilliT Server ── E2E encrypted ──\u003e Phone B\n              (Signal Protocol)       (relay only)       (Signal Protocol)\n```\n\n**What makes it different:**\n- **No phone number, no email** — authentication via cryptographic key pair\n- **No central server** — each instance is independent, you own everything\n- **Tor built-in** — server and mobile app support `.onion` natively, zero config\n- **Runs on a Raspberry Pi** — ~100-150MB RAM, SQLite, no external dependencies\n\n## Quick Start\n\nOne command to install (Docker required on Linux, Docker Desktop on macOS):\n\n```bash\n# Linux / Raspberry Pi\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/scripts/install.sh -o /tmp/tillit-install.sh \u0026\u0026 sudo bash /tmp/tillit-install.sh\n\n# macOS (no sudo needed)\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/scripts/install.sh -o /tmp/tillit-install.sh \u0026\u0026 bash /tmp/tillit-install.sh\n```\n\n\u003e The installer is interactive. Download-then-run (instead of `curl ... | bash`) keeps your\n\u003e terminal attached to the script, so the prompts work correctly.\n\nThe interactive installer asks how you want to expose your server:\n\n| Option | What it does | Needs |\n|--------|-------------|-------|\n| **Tor Hidden Service** (default) | Anonymous `.onion` address, zero config | Nothing |\n| **Cloudflare Tunnel** | Public HTTPS, no port forwarding | Cloudflare account (free) |\n| **HTTPS** | Let's Encrypt via DNS-01 | TilliT Cloud credentials |\n| **HTTP** | Plain HTTP on custom port | Port forwarding / VPN |\n\nAfter install, use `tillit status` to check your server and `tillit help` for all commands.\n\n**Raspberry Pi?** See the [zero-terminal setup guide](docs/raspberry-pi-setup.md) — flash the SD card, run one script, plug in the Pi, done.\n\n## Mobile App\n\nThe TilliT app is in open beta with **native Tor support** (arti on iOS, ctor on Android) — no extra apps or proxies needed.\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://testflight.apple.com/join/9QFM35GD\"\u003e\u003cimg src=\"https://img.shields.io/badge/TestFlight-Join%20Beta-blue?logo=apple\u0026logoColor=white\u0026style=for-the-badge\" alt=\"TestFlight\"\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\n  \u003ca href=\"https://play.google.com/store/apps/details?id=com.oglut.tillit.xnative\"\u003e\u003cimg src=\"https://img.shields.io/badge/Google%20Play-Join%20Beta-green?logo=googleplay\u0026logoColor=white\u0026style=for-the-badge\" alt=\"Google Play\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nInstall the app, point it at your server address (IP, domain, or `.onion`), and start chatting. The app handles key generation, session setup, and encryption transparently.\n\n## What works today\n\n- End-to-end encrypted 1:1 and group messaging (Signal Protocol)\n- Room-based chat with invite codes\n- Encrypted media (photos, files) with optional ephemeral mode\n- Offline message queue (7-day TTL, delivered on reconnect)\n- Tor Hidden Service with native client support\n- Cloudflare Tunnel and HTTPS deployment modes\n- DDNS with automatic `\u003cbox-id\u003e.tillit.cc` domain\n- Push notifications (iOS + Android)\n- CLI management tool (`tillit status`, `tillit logs`, `tillit update`)\n- Bare-metal and Docker installation on Linux, macOS, Raspberry Pi\n\n## Who this is for\n\n- People who want messaging without giving up a phone number\n- Privacy enthusiasts who want to control their own infrastructure\n- Small groups (family, team, friends) who want a private channel\n- Developers interested in Signal Protocol implementations\n- Anyone curious about self-hosted alternatives to centralized chat\n\n---\n\n## Technical Details\n\nEverything below is for developers and contributors. If you just want to use TilliT, the [Quick Start](#quick-start) and [Mobile App](#mobile-app) sections above are all you need.\n\n### Architecture\n\n```\n┌─────────────┐       E2E encrypted         ┌─────────────┐\n│  TilliT App │ \u003c────────────────────────── │  TilliT App │\n│  (iOS/And)  │                             │  (iOS/And)  │\n└──────┬──────┘                             └──────┬──────┘\n       │ WSS                                       │ WSS\n       └──────────────────┬────────────────────────┘\n                          │\n                ┌─────────┴─────────┐\n                │   TilliT Backend  │\n                │   (this repo)     │\n                │                   │\n                │  - Message relay  │\n                │  - Key storage    │\n                │  - Offline queue  │\n                │  - Media storage  │\n                │  - DDNS client    │\n                │                   │\n                │  SQLite | MariaDB │\n                └───────────────────┘\n```\n\n### Encryption\n\nTilliT implements the Signal Protocol for end-to-end encryption:\n\n- **Double Ratchet** with X3DH key agreement for forward secrecy\n- **Kyber post-quantum keys** for future-proofing against quantum attacks\n- **Sender keys** for efficient group messaging (single ciphertext per message)\n- **Challenge-response authentication** — no passwords, Ed25519 key signatures\n- **Zero-knowledge server** — stores only public keys, relays opaque encrypted envelopes\n\nSee [`docs/signal-protocol.md`](docs/signal-protocol.md) for the full security architecture.\n\n### Network Modes\n\n| Mode | Compose file | Access | Port forwarding | External account |\n|------|-------------|--------|-----------------|------------------|\n| **Tor Hidden Service** | `docker-compose.tor.yml` | `http://\u003chash\u003e.onion` | No | No |\n| **Cloudflare Tunnel** | `docker-compose.tunnel.yml` | `https://\u003cdomain\u003e` | No | Cloudflare |\n| **HTTPS** | `docker-compose.https.yml` | `https://\u003cbox\u003e.tillit.cc` | Yes | TilliT Cloud |\n| **HTTP** | `docker-compose.selfhosted.yml` | `http://\u003cip\u003e:3000` | Yes | No |\n\n#### Tor Hidden Service\n\n```bash\nmkdir -p /opt/tillit \u0026\u0026 cd /opt/tillit\n\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/docker-compose.tor.yml -o docker-compose.yml\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/Dockerfile.tor -o Dockerfile.tor\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/.env.selfhosted.sample -o .env\n\ndocker compose up -d\n\n# Wait ~60s for Tor bootstrap, then get your .onion address\ndocker compose exec tor cat /var/lib/tor/hidden_service/hostname\n```\n\n**How it works:**\n\n```\nClient ──Tor──\u003e .onion:80 ──\u003e tor container ──\u003e tillit:3000\n                               (sidecar)        (internal only)\n```\n\n- The `tillit` container runs on an **internal Docker network** with no internet access\n- The `tor` sidecar bridges the Tor network to tillit\n- Port 3000 is bound to `127.0.0.1` only (local health checks, not exposed externally)\n- Cloud services are disabled to prevent clearnet IP leaks\n\nSee [`docs/self-hosted-dns.md`](docs/self-hosted-dns.md) for all network options in detail.\n\n### Deployment Modes\n\n| Mode | Database | Redis | Target |\n|------|----------|-------|--------|\n| `selfhosted` | SQLite | No | Raspberry Pi, mini-PC, single-board |\n| `cloud` | MariaDB | Yes | AWS EKS, Kubernetes, multi-instance |\n\nSet via `DEPLOYMENT_MODE` environment variable (default: `cloud`).\n\n### Installation Options\n\n#### Docker (manual)\n\n```bash\nmkdir -p /opt/tillit \u0026\u0026 cd /opt/tillit\n\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/docker-compose.selfhosted.yml -o docker-compose.yml\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/.env.selfhosted.sample -o .env\n\nnano .env\ndocker compose up -d\ncurl http://localhost:3000/health\n```\n\n#### Bare-metal (no Docker)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/tillit-cc/tillit/main/scripts/install-bare.sh -o /tmp/tillit-install-bare.sh \u0026\u0026 sudo bash /tmp/tillit-install-bare.sh\n```\n\n#### Development\n\n```bash\ngit clone https://github.com/tillit-cc/tillit.git\ncd tillit\npnpm install\n\ncp .env.sample .env\n\nmkdir -p keys\nopenssl genrsa -out keys/private.pem 2048\nopenssl rsa -in keys/private.pem -pubout -out keys/public.pem\n\npnpm run start:dev\n```\n\n### API Reference\n\n#### REST Endpoints\n\nAll endpoints except `/auth/challenge` and `/auth/identity` require JWT authentication via `Authorization: Bearer \u003ctoken\u003e`.\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `POST` | `/auth/challenge` | Request authentication challenge nonce |\n| `POST` | `/auth/identity` | Authenticate with signed challenge |\n| `GET` | `/auth/token/refresh` | Refresh JWT token |\n| `POST` | `/auth/token/push` | Register push notification token |\n| `GET` | `/auth/v1/users/me` | Get current user |\n| `PUT` | `/chat` | Create new room |\n| `POST` | `/chat/:code` | Join room by invite code |\n| `GET` | `/chat/:id/members` | Get room members |\n| `DELETE` | `/chat/:id` | Delete/leave room |\n| `GET` | `/chat` | Get all rooms |\n| `POST` | `/keys` | Upload Signal Protocol keys |\n| `GET` | `/keys/:userId` | Get key bundle for user |\n| `GET` | `/keys/status/self` | Get own key status |\n| `POST` | `/media/upload` | Upload encrypted media |\n| `GET` | `/media/:id` | Download encrypted media |\n| `GET` | `/health` | Health check |\n\n#### WebSocket Events\n\nConnect to `/chat` namespace with `Bearer \u003ctoken\u003e` in auth.\n\n**Client -\u003e Server:**\n\n| Event | Payload | Description |\n|-------|---------|-------------|\n| `sendMessage` | `{ roomId, message, category?, type?, volatile? }` | Send encrypted message |\n| `sendPacket` | `{ roomId, packet, recipientIds? }` | Send control packet |\n| `joinRoom` | `{ roomId }` | Join room's WebSocket channel |\n| `leaveRoom` | `{ roomId }` | Leave room's WebSocket channel |\n\n**Server -\u003e Client:**\n\n| Event | Description |\n|-------|-------------|\n| `newMessage` | Incoming message envelope |\n| `newPacket` | Incoming control packet |\n| `userJoined` | User joined room |\n| `userLeft` | User left room |\n| `userOnline` | User came online |\n\n### Configuration\n\nSee [`.env.sample`](.env.sample) for cloud mode and [`.env.selfhosted.sample`](.env.selfhosted.sample) for self-hosted mode.\n\nKey environment variables:\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `DEPLOYMENT_MODE` | `cloud` or `selfhosted` | `cloud` |\n| `APP_PORT` | HTTP port | `3000` |\n| `JWT_EXPIRES_IN` | JWT token expiry | `7d` |\n| `CORS_ORIGIN` | Allowed origins (`true` for all) | `true` |\n| `DDNS_ENABLED` | Enable DDNS registration | `false` |\n| `MEDIA_MAX_SIZE` | Max upload size (bytes) | `10485760` |\n| `MEDIA_RETENTION_DAYS` | Days before media cleanup | `30` |\n\n### Project Structure\n\n```\nsrc/\n├── auth/                  # Authentication (JWT, challenge-response)\n├── config/                # Configuration modules (app, jwt, ddns, media)\n├── database/              # Database adapters (MariaDB, SQLite), migrations\n├── entities/              # TypeORM entities\n├── modules/\n│   ├── chat/              # Chat module (gateway, controllers, services)\n│   ├── keys/              # Signal Protocol key management\n│   ├── sender-keys/       # Sender key distribution for groups\n│   ├── media/             # Encrypted media blob storage\n│   └── ddns/              # Dynamic DNS client\n├── services/              # Shared services (push notifications)\n├── sockets/               # WebSocket adapter with authentication\n└── main.ts                # Application entry point\n```\n\n### Security\n\n- **Signal Protocol** — industry-standard E2E encryption with forward secrecy\n- **Challenge-response auth** — no passwords, authentication via Ed25519 key signatures\n- **Server-side rate limiting** — ThrottlerGuard on all endpoints\n- **Input validation** — ValidationPipe on all REST and WebSocket handlers\n- **Atomic operations** — Redis GETDEL for one-time challenge consumption\n- **Path traversal protection** — media storage validates all file paths\n- **Tor network isolation** — in onion mode, the backend has no direct internet access\n\nSee [SECURITY.md](SECURITY.md) for our security policy and how to report vulnerabilities.\n\n## Roadmap\n\nSee [ROADMAP.md](ROADMAP.md) for planned features, including server management via app and server federation.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## License\n\nThis project is licensed under the [AGPL-3.0](LICENSE) license.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftillit-cc%2Ftillit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftillit-cc%2Ftillit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftillit-cc%2Ftillit/lists"}