Chat with your friends and family from any device.
- Tech and tools
- External services
- Project structure
- End-to-end encryption
- WebSockets
- Installation
- Environment variables
- Database setup
- Email setup
- Developing
- Deployment
- Visualize bundle
- List of commands
App
- Svelte: Front-end library that compiles declarative components into fast and lean web apps.
- SvelteKit: Framework for rapidly developing robust, performant web applications using Svelte.
- TailwindCSS: Utility-first, component driven CSS framework.
- tRPC: End-to-end typesafe APIs made easy.
WebSocket
- Socket.io: Library that enables low-latency, bidirectional and event-based communication between a client and a server.
Database
- PostgreSQL: Powerfull, open source object-relational database system.
- DrizzleORM: TypeScript ORM that feels like writing SQL.
Authentication
- Lucia: Simple and flexible auth library for TypeScript.
- Nodemailer: Send emails from Node.js.
- Svelte-email: Build emails using Svelte components.
Testing
- Vitest: Next generation testing framework powered by Vite.
- Playwright: Reliable end-to-end testing for modern web apps.
/
├── drizzle/
│ └── SQL migrations.
├── tests/
│ └── Tests.
├── s3/
│ └── Fake S3 server used for development.
├── static/
│ └── Static assets to be served.
│
├── src/
│ ├── lib/
│ │ ├── auth/
│ │ │ └── Authentication using lucia.
│ │ ├── db/
│ │ │ └── Database config, schemas and queries using drizzle.
│ │ ├── trpc/
│ │ │ └── tRPC config, routers and procedures.
│ │ ├── socket/
│ │ │ └── WebSockets using socket.io.
│ │ ├── email/
│ │ │ └── Email config, templates and functions.
│ │ ├── file-upload/
│ │ │ └── File uploads using Amazon S3 SDK.
│ │ ├── ui/
│ │ │ └── Reusable UI components.
│ │ ├── actions/
│ │ │ └── Reusable element behaviors.
│ │ └── utils/
│ │ └── Helper functions.
│ │
│ └── routes/
│ └── Routes of your application and co-located components and files.
│
└── Config files.
End-to-End Encryption (E2EE) ensures that only the intended participants can access the content of a conversation. Because the content of the messages is encrypted using a shared secret that only both ends know, no one else can read the messages, not even the server.
This project uses the native Web Crypto API to
implement end-to-end encryption. You can find the implementation at src/lib/utils/encryption.ts
.
Elliptic-curve Diffie–Hellman (ECDH) is an anonymous key agreement protocol that allows two parties, each having an elliptic-curve public–private key pair, to establish a shared secret over an insecure channel.
We use the P-256 curve, which is the most widely supported curve in browsers, and has a good balance between security and performance.
AES-GCM is an authenticated encryption with associated data (AEAD) cipher. It is based on the Advanced Encryption Standard (AES) algorithm and the Galois/Counter Mode (GCM) block mode. It provides both encryption and message authentication.
The simplistic implementation of end-to-end encryption in this project is based on the following steps:
-
User generates a public/private key pair.
-
User sends their public key, and the private key encrypted with their password, to the server. Account is created.
-
User logs in. Server sends the encrypted private key to the user.
-
User decrypts the private key with their password.
-
User generates a shared secret using their private key and the other user's public key. (Diffie-Hellman key exchange)
-
User stores shared secrets locally. (localStorage)
-
Both users in chat encrypt and decrypt messages using the shared secret.
-
User logs out. Private and shared keys are deleted from localStorage.
This implementation is not perfect, and it has some flaws:
- One private key per account.
- One shared secret per chat.
- No key rotation.
- No forward secrecy.
This project uses Socket.io to enable real-time, bidirectional and event-based communication between the client and the server.
As of today, SvelteKit does not have native support for WebSockets.
One solution is to create a separate server, but we can build a solution to integrate it into our SvelteKit server.
In development, we use a simple vite plugin to hook into the development server.
(See vite.config.js
)
With SvelteKit node adapter, we can create a custom server to handle WebSockets.
(See server.js
)
Clone this repository:
git clone https://github.com/marioperezhurtado/boble2.git
cd boble2
Install dependencies:
npm install
To run this project, you will need to add the following environment variables to your .env
file
# Private
# SMTP
SENDGRID_API_KEY=""
SENDER_ADDRESS=""
SENDER_NAME=""
# GIF
GIPHY_API_KEY=""
# S3
S3_ACCESS_KEY_ID="S3RVER"
S3_SECRET_ACCESS_KEY="S3RVER"
# Public
PUBLIC_SITE_URL="http://localhost:5173"
PUBLIC_S3_URL="http://localhost:5000"
PUBLIC_S3_BUCKET_URL="http://localhost:5000/<bucket-name>"
You can create a copy of the .env.example
file to .env
and populate it with your secrets.
cp .env.example .env
This project uses PostgreSQL and DrizzleORM.
-
Install postgres in your local machine.
-
Log-in to and create your database.
sudo -u postgres psql
CREATE DATABASE db_name;
- Populate your .env with your database connection string.
DATABASE_URL="postgres://username:password@host:port/db_name",
The first time you run this project, and every time you make a change in your db schema, you will neeed to push the changes to your database (or create one if it doesn't exist yet):
npm run db:push
You can check your tables and data using drizzle studio:
npm run db:studio
This project uses Nodemailer to send emails, and SendGrid as a SMTP provider.
There are several alternatives that you could use as SMPT provider (Mailgun, AWS SES, etc).
If you choose to use SendGrid:
- Create your account.
- Create a verified single sender. (You can choose a real email, or setup your domain, e.g. noreply@your-domain.com)
- Create your API key.
- Populate your .env.
If you choose a different provider, you might need to change the email config at
src/lib/email/email.ts
.
Once you've created a project and installed dependencies with npm install (or pnpm, yarn, bun etc), populated your environment variables and followed the previous steps,
Start a development server:
npm run dev
Start a fake S3 server:
npm run s3
This project is deployed to Railway.
- Create an account in AWS.
- Create a new S3 bucket with public access.
- Create a new IAM user with programmatic access and S3 full access.
- Set up your bucket policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<bucket-name>/*"
}
]
}
- Set up your CORS configuration:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"POST",
"DELETE"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
- Copy your access key and secret key to your
.env
file.
Depending on where you deploy to, this part can be different.
In Railway:
- Go to your project.
- Right click > Add New Service > Database.
- Add PostgreSQL.
- Click on your repository > Variables > Shared Variable.
- Add DATABASE_URL.
- Deploy.
This project uses rollup-plugin-visualizer to analyze your bundle and see which modules are taking up space.
To visualize your bundle, run:
npm run build
Then open the generated html file in your browser. You can find it
in .svelte-kit/output/client/stats.html
.
Command | Description |
---|---|
dev | Start a development server on port 5173 |
build | Create a production version of your app |
preview | Preview your production build |
start | Start a production server |
s3 | Start a fake S3 server on port 5000 |
check | Run diagnostic checks |
lint | Lint your project |
format | Format your project |
db:generate | Generate a SQL migration file |
db:push | Push your changes to your database |
db:studio | Launch a visual editor for your database on local.drizzle.studio |
test | Run all tests |
test:unit | Run unit tests |
test:integration | Run integration tests |