# NymDrop — Architecture v2

## Principio fondante

**La protezione è nel design, non nell'obbligo di installare qualcosa.
Chi vuole anonimato aggiuntivo usa quello che trova.**

La fonte usa un browser qualsiasi. Il documento viaggia interamente su Nym.
Il server è un relay cieco — non conserva nulla, non sa cosa ha trasmesso.

---

## Flusso completo

```
[FONTE]                    [SERVER NYMDROP]              [GIORNALISTA]
  │                               │                           │
  │  HTTPS (browser normale)      │                           │
  ├──────────────────────────────▶│                           │
  │                               │                           │
  │                     WebCrypto nel browser:                │
  │                     cifra con chiave pubblica             │
  │                     X25519 del giornalista                │
  │                               │                           │
  │  POST ciphertext              │                           │
  ├──────────────────────────────▶│                           │
  │                               │                           │
  │                     riceve ciphertext                     │
  │                     NON lo salva su disco                 │
  │                               │                           │
  │                     nym-client embedded:                  │
  │                     invia ciphertext al                   │
  │                     Nym address del giornalista           │
  │                               │                           │
  │                               │    Nym mixnet (multi-hop) │
  │                               │    cover traffic + pad    │
  │                               ├──────────────────────────▶│
  │                               │                           │
  │                     cancella ciphertext                   │
  │                     dalla RAM                             │
  │                               │                 ciphertext arriva
  │                               │                 al Nym address
  │                               │                 del giornalista
  │                               │                           │
  │                               │                 giornalista decifra
  │                               │                 localmente con
  │                               │                 chiave privata X25519
  │                               │                           │
  │  conferma generica "ok"       │                           │
  │◀──────────────────────────────│                           │
```

---

## Componenti

### 1. Frontend (browser della fonte)

- HTML + CSS puro, zero framework JS, zero CDN, zero cookie
- **WebCrypto API** nativa del browser — nessuna libreria esterna:
  - Chiave pubblica X25519 del giornalista embedded nel HTML al momento del deploy
  - Genera keypair effimera X25519 per la sessione
  - ECDH key exchange → shared secret
  - Deriva chiave ChaCha20-Poly1305 via HKDF
  - Cifra testo + file nel browser
  - Manda solo ciphertext + public key effimera al server
- Il plaintext non lascia mai il browser in chiaro
- Form: textarea testo libero + upload file opzionale + submit
- Funziona su qualsiasi browser moderno

### 2. Backend Go — relay cieco

```
nymdrop/
├── cmd/
│   └── nymdrop/
│       └── main.go              ← entrypoint
├── internal/
│   ├── handler/
│   │   ├── submit.go            ← POST /submit
│   │   └── static.go            ← serve HTML/CSS/JS
│   ├── relay/
│   │   └── relay.go             ← ricevi → invia su Nym → cancella
│   ├── nym/
│   │   └── client.go            ← wrapper nym client embedded
│   └── nolog/
│       └── middleware.go        ← strip IP, UA, headers — zero log
├── static/
│   ├── index.html               ← form submission
│   └── crypto.js                ← WebCrypto lato client
├── go.mod
└── CLAUDE.md
```

**Comportamento del relay:**
```
ricevi ciphertext da HTTP POST
    ↓
passa a nym/client.go → Send(journalistNymAddress, ciphertext)
    ↓
Nym mixnet consegna al giornalista
    ↓
cancella ciphertext dalla memoria (zeroing)
    ↓
rispondi HTTP 200 "ok"
```

Zero scritture su disco. Zero database. Zero log.

### 3. Nym client embedded lato server

Il server incorpora `nym-socks5-client` come subprocess gestito:

```go
// nym/client.go
type NymClient struct {
    addr    string   // Nym address del giornalista
    process *exec.Cmd
    socks5  string   // localhost:1080
}

func (c *NymClient) Send(payload []byte) error {
    // connessione SOCKS5 → Nym mixnet → giornalista
    conn, _ := proxy.SOCKS5("tcp", c.socks5, nil, proxy.Direct)
    // ...
}
```

Evoluzione futura: FFI bindings Rust/Go via `sdk/ffi/shared/` — client in-process, zero subprocess.

### 4. Lato giornalista

Il giornalista ha un **Nym address** dedicato a NymDrop.
Riceve i documenti cifrati direttamente sul suo client Nym (NymConnect o nym-socks5-client locale).
Decifra localmente con la sua chiave privata X25519 — il server non è mai coinvolto nella decifrazione.

Nessuna interfaccia admin sul server — il server non ha nulla da mostrare perché non conserva nulla.

---

## No-log by design

```go
func NoLog(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        r.Header.Del("X-Forwarded-For")
        r.Header.Del("X-Real-IP")
        r.Header.Del("User-Agent")
        r.RemoteAddr = "0.0.0.0:0"
        next.ServeHTTP(w, r)
    })
}
```

- Zero access log
- Zero error log con IP
- Zero timestamp salvati
- Rate limiting via token bucket anonimo (contatore in RAM, reset periodico)

---

## Threat model

| Minaccia | Protetto? | Come |
|---|---|---|
| Server legge documento | ✅ | Cifrato WebCrypto lato client — server vede solo ciphertext |
| Server logga IP fonte | ✅ | No-log middleware, zero access log |
| Sequestro server | ✅ | Zero documenti su disco — relay puro, inbox sempre vuota |
| Traffic analysis fonte→server | ⚠️ | HTTPS normale — fonte usa quello che trova |
| Traffic analysis server→giornalista | ✅ | Nym mixnet multi-hop, cover traffic, padding |
| Correlazione timing | ✅ | Nym Loopix delays randomizzati |
| Intercettazione documento in transito | ✅ | ChaCha20-Poly1305, solo destinatario decifra |
| Compromissione chiave privata giornalista | ❌ | Fuori scope — responsabilità del giornalista |

**Punto chiave per il grant:** sequestro del server = zero documenti recuperabili. Il server è matematicamente incapace di rivelare il contenuto delle submission — non perché lo voglia, ma perché non lo ha mai avuto.

---

## Deploy single container

```bash
# Proxmox — CT125
pct create 125 /var/lib/vz/template/cache/debian-12.tar.zst \
  --hostname nymdrop --memory 512 --cores 1

# Inside container
apt install -y golang-go
git clone https://github.com/gabrix73/nymdrop
cd nymdrop
go build ./cmd/nymdrop

# Config
cat > /etc/nymdrop/config.toml << EOF
listen = ":443"
journalist_nym_address = "<NYM_ADDRESS>"
journalist_pubkey = "<X25519_PUBLIC_KEY_HEX>"
tls_cert = "/etc/ssl/nymdrop/cert.pem"
tls_key = "/etc/ssl/nymdrop/key.pem"
EOF

./nymdrop --config /etc/nymdrop/config.toml
```

---

## Struttura repo

```
gabrix73/nymdrop
├── cmd/nymdrop/
├── internal/
├── static/
├── docs/
│   ├── ARCHITECTURE.md
│   ├── THREAT_MODEL.md
│   └── DEPLOY.md
├── CLAUDE.md
├── LICENSE               ← AGPL-3.0
├── README.md
└── go.mod
```

---

## Pitch grant (sintesi)

NymDrop è il primo sistema di whistleblowing dove:
- La fonte usa un browser qualsiasi — zero installazioni
- Il documento viaggia end-to-end su Nym mixnet
- Il server è un relay cieco — zero storage, zero metadati
- Sequestro del server = zero documenti recuperabili
- Resistente a traffic analysis globale (Loopix delays, cover traffic)

Nessun sistema esistente combina questi quattro punti simultaneamente.

---

## Prossimo passo

Scaffolding Go: `main.go` + `nolog/middleware.go` + `handler/submit.go` + `static/index.html` + `static/crypto.js`
