···11+# Contributing to Tranquil PDS
22+33+## Local Development
44+55+### Prerequisites
66+77+- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
88+- Add `pds.test` to your hosts file (one-time setup):
99+1010+ ```
1111+ 127.0.0.1 pds.test
1212+ ```
1313+1414+ - **macOS / Linux:** `/etc/hosts`
1515+ - **Windows:** `C:\Windows\System32\drivers\etc\hosts`
1616+1717+### Starting the dev environment
1818+1919+```bash
2020+just run-dev
2121+```
2222+2323+This starts the following services via `docker-compose`:
2424+2525+- **Traefik** — HTTPS reverse proxy at `https://pds.test`
2626+- **Backend** — Rust server with `cargo-watch` (auto-rebuilds on file changes)
2727+- **Frontend** — Vite dev server with hot module replacement
2828+- **Postgres** — Database on port 5432
2929+- **PLC Directory** — Local [did-method-plc](https://github.com/did-method-plc/did-method-plc) server for DID registration
3030+- **Mailpit** — Local email server with web UI at [http://localhost:8025](http://localhost:8025)
3131+3232+Once all services are running, open **https://pds.test** in your browser.
3333+3434+### Trusting the self-signed certificate
3535+3636+Traefik generates a self-signed TLS certificate. Your browser will show a security warning on first visit. You can either click through it, or add the certificate to your system trust store for a seamless experience:
3737+3838+**macOS:**
3939+4040+```bash
4141+# Extract the cert from traefik and add it to the system keychain
4242+echo | openssl s_client -connect localhost:443 -servername pds.test 2>/dev/null | openssl x509 > /tmp/pds-test.pem
4343+sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/pds-test.pem
4444+```
4545+4646+**Linux (Debian/Ubuntu):**
4747+4848+```bash
4949+echo | openssl s_client -connect localhost:443 -servername pds.test 2>/dev/null | openssl x509 | sudo tee /usr/local/share/ca-certificates/pds-test.crt
5050+sudo update-ca-certificates
5151+```
5252+5353+**Linux (Fedora/RHEL):**
5454+5555+```bash
5656+echo | openssl s_client -connect localhost:443 -servername pds.test 2>/dev/null | openssl x509 | sudo tee /etc/pki/ca-trust/source/anchors/pds-test.pem
5757+sudo update-ca-trust
5858+```
5959+6060+**Windows (PowerShell as Administrator):**
6161+6262+```powershell
6363+$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
6464+$cert.Import([System.Text.Encoding]::UTF8.GetBytes((echo | openssl s_client -connect localhost:443 -servername pds.test 2>$null | openssl x509)))
6565+$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine")
6666+$store.Open("ReadWrite")
6767+$store.Add($cert)
6868+$store.Close()
6969+```
7070+7171+Restart your browser after adding the certificate.
7272+7373+### Stopping the dev environment
7474+7575+```bash
7676+# Stop containers (preserves database + build cache)
7777+docker compose --profile dev down
7878+7979+# Stop and wipe all data (fresh start)
8080+docker compose --profile dev down -v
8181+```
8282+8383+### Direct database access
8484+8585+Postgres is exposed on port 5432:
8686+8787+```bash
8888+psql postgres://postgres:postgres@localhost:5432/pds
8989+```
9090+9191+### How it works
9292+9393+- **Source code** is bind-mounted into the containers so that changes made on the host will be immediately reflected in the application
9494+- **Backend** uses `cargo-watch` to recompile and restart when Rust files change
9595+- **Frontend** uses Vite's HMR for instant browser updates when frontend files change
9696+- **Build cache** (`target/` directory and cargo registry) are stored in Docker volumes, so incremental compilation persists across container restarts
9797+- **Traefik** routes `/`, `/xrpc`, `/oauth`, `/.well-known`, `/u`, and `/health` to the backend; everything else goes to the Vite dev server
9898+- **Mailpit** captures all outgoing email — open [http://localhost:8025](http://localhost:8025) to view verification emails during registration
9999+- **PLC Directory** runs locally so DID registration doesn't hit the real `plc.directory`
100100+101101+### Running the backend natively
102102+103103+If you prefer running the Rust backend outside Docker (faster incremental builds on host), you need:
104104+105105+- Rust toolchain (see `rust-toolchain.toml`)
106106+- `protoc` (`brew install protobuf` on macOS)
107107+- PostgreSQL (start with `docker compose up db`)
108108+109109+Then run:
110110+111111+```bash
112112+cargo run -p tranquil-server -- --config config.toml
113113+```
114114+115115+And start the frontend separately:
116116+117117+```bash
118118+cd frontend && pnpm install && pnpm dev
119119+```