# pvtcoms relay — secure VPS setup

This bundle turns a fresh **Ubuntu 24.04 LTS** VPS into a hardened **oblivious relay**: a blind
store-and-forward + directory node reached **only over Tor** (a `.onion` service). It never sees user
IPs, message content, identities, or who talks to whom — it stores opaque blobs under rotating tokens
and burns them on read. See [`../THREAT_MODEL.md`](../THREAT_MODEL.md) and
[`../docs/ADR/004-multi-transport-per-modality.md`](../docs/ADR/004-multi-transport-per-modality.md).

## What runs on the box

```
            Tor network
 user ───────(onion)───────▶  relay.onion ──▶ 127.0.0.1:9911  (pvtcoms relay daemon)
                                                  deposit / pull (burn-on-read)
                                                  directory records (signed, per-contact-encrypted)
```

- **Relay = onion-only.** The daemon binds to `127.0.0.1` and Tor publishes the onion. There is **no
  public messenger port**. The firewall denies *all* inbound except SSH.
- **Clearnet is never used to reach the relay.** Direct clearnet is only ever **peer-to-peer** (audio/
  video), negotiated *after* both peers pull each other's address record from the relay over Tor.

## Pick a host (do this BEFORE buying)

Any provider works **if it passes these three checks** — confirm with sales chat / ToS first:

1. **Full root access** (needed for LUKS, Tor, firewall, systemd sandboxing).
2. **KVM virtualization, not OpenVZ** (OpenVZ often blocks Tor / raw sockets and can't do FDE).
3. **ToS permits running Tor / onion services** ← the one that gets cheap website-hosts wrong.

A `1 vCPU / 2 GB / 50 GB` box is far more than enough (blobs are tiny and burned fast).

| Host | Verdict |
|---|---|
| **Contabo (Amsterdam)** | Cheap, full KVM root, NL jurisdiction, Tor-friendly. Good default. |
| **Bluehost (Amsterdam)** | Cheapest you found; **only if it passes all 3 checks** (website-hosts often fail #3). Don't pre-pay 2 years until confirmed. |
| **IONOS** | Fine, EU/UK. |
| **1984 (Iceland) / Njalla** | Privacy-aligned, pricier; best jurisdiction if budget allows. |

**Jurisdiction matters** for a privacy tool — prefer EU/Iceland over US/UK where practical.

## Run order (on the VPS, as root on first login)

```bash
# 0. Get the files onto the box (scp this deploy/ folder, or git clone the repo)
scp -r deploy/ root@<vps-ip>:/root/pvtcoms-deploy

# 1. Harden the OS: non-root sudo user, SSH key-only, UFW deny-all-but-SSH, fail2ban, auto-updates
bash /root/pvtcoms-deploy/harden.sh

#    ⚠ harden.sh prints the new SSH user. RECONNECT as that user before continuing,
#    and confirm key login works — do NOT close your root session until you have.

# 2. Install Tor + publish the relay onion + install the daemon as a sandboxed systemd service
sudo bash /root/pvtcoms-deploy/setup-relay.sh

# 3. Read the relay's .onion address (this is what gets baked into the app):
sudo cat /var/lib/tor/pvtcoms-relay/hostname
```

## Security rules honoured here (see [`../CLAUDE.md`](../CLAUDE.md))

- **Never** paste root passwords or private keys into chat or commit them. SSH **keys only**.
- The onion private key lives in `/var/lib/tor/pvtcoms-relay/` (mode 0700, tor user) — **never** leaves
  the box, **never** committed. Back it up encrypted if you want a stable address across rebuilds.
- No access logs, no metadata retention. Burn-on-read. Optional full-disk LUKS (see `harden.sh` notes).
- Fail closed: the daemon binds localhost only; if Tor is down, nothing is reachable (no clearnet leak).

## Access control (gated by default)

`setup-relay.sh` generates a 32-byte **shared access key** and stores it in `/etc/pvtcoms-relay.env`
(root, mode 0600). The relay runs **gated**: only requests carrying a valid capability derived from
that key are accepted — strangers are rejected at the door, yet the relay still **cannot tell members
apart** (obliviousness preserved; see [`../docs/ADR/005-relay-access-and-anti-abuse.md`](../docs/ADR/005-relay-access-and-anti-abuse.md)).

- **Distribute the key** to invited members only (it gets baked into their app build / shared
  out-of-band). The script prints it at the end. Treat it like a password — never commit it.
- **Run an OPEN/public relay** instead by setting `PVTCOMS_RELAY_KEY=` (empty) in the env file and
  `systemctl restart pvtcoms-relay`. Then anyone may use it; **proof-of-work** still throttles abuse.
- **Anti-abuse**: every request must carry a hashcash **proof-of-work** stamp (default 20 bits) — an
  identity-free flood throttle that applies in both gated and open mode.
- **Revocation**: the shared key is not individually revocable — to remove someone, rotate the key
  (regenerate the env file) and redistribute to the rest. (Per-user unlinkable revocation needs
  anonymous credentials — future work, ADR-005.)

Tune via `/etc/pvtcoms-relay.env`: `PVTCOMS_RELAY_POW` (difficulty), `PVTCOMS_RELAY_TTL` (retention
seconds), then `systemctl restart pvtcoms-relay`.

## Production status

The relay daemon is now production-grade: **gated access** (or open+PoW), **proof-of-work**
anti-abuse, **freshness + replay** suppression, **TTL** expiry, **keep-alive** framing (many requests
per connection), and **atomic on-disk persistence** (mail survives a restart). All covered by tests
(`cargo test -p pvtcoms-core relay`, plus the engine's deposit/pull/persistence suite).

Remaining for the wider system (separate slices, not relay-internal): the **directory records**
(signed, per-contact-encrypted address records) so the app connects by **identity** instead of a
pasted address, and the app-side integration of relay send/recv into the GUI.
