Metadata-Version: 2.4
Name: mess-protocol
Version: 0.0.2
Summary: Protocollo di comunicazione sicura tra microservizi
Author: Lorenzo Moglia
Author-email: lorenzomoglia@gmail.com
Requires-Python: >=3.12
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: cryptography (>=42.0)
Description-Content-Type: text/markdown

# MESS

[ ] Un protocollo di comunicazione tra microservizi  
[ ] Un esercizio di ragionamento per scrivere codice senza AI(*) e restare a contatto con il prodotto  
[ ] Un modo pratico di rispolverare le logiche di rete a basso livello e la loro implementazione in python  
[X] Tutte le  

*in real

MESS riprende lo stile di HTTP/1.1 (header testuali separati da `\r\n`, body
binario, `Content-Length` per il framing, connessioni keep-alive) ma aggiunge
un meccanismo di auditing **non aggirabile per costruzione crittografica**:
ogni Request viene cifrata con una chiave per-messaggio depositata al
Compliance Collector. Il Receiver puo decifrarla solo dopo aver ottenuto la
chiave dal Collector, che la rilascia solo se la Compliance e stata registrata.

Se il Collector e giu, la chiave non viene mai depositata e il ciphertext resta
matematicamente indecifrabile: fail-stop, niente "best-effort" sull'audit.

## Flusso end-to-end

```
sender                    collector                receiver
  |                          |                        |
  |-- 1. Pre-Compliance ---->|                        |
  |       (key, nonce, md)   |                        |
  |<------- 2. Ack ----------|                        |
  |                          |                        |
  |-------- 3. Encrypted Request (ciphertext) ------->|
  |                          |                        |
  |                          |<-- 4. KEY_FETCH -------|
  |                          |       (request_id)     |
  |                          |---- 5. {key,nonce} --->|
  |                          |                        |
  |                          |              [decifra + handler]
  |                          |                        |
  |<------------ 6. Response (plaintext) -------------|
  |                          |                        |
  |                          |<-- 7. Post-Compliance -|
  |                          |   (fire-and-forget)    |
```

Il diagramma editabile e in [`docs/sequence.excalidraw`](docs/sequence.excalidraw)
(apribile su [excalidraw.com](https://excalidraw.com) o nelle estensioni IDE).

## Formato del messaggio

Newline `\r\n`, doppio `\r\n` tra header e body.

### Request / Response / Error / KeyFetch

```
MESS/1.0
Mess-Event: Request | Response | Error | KeyFetch | Compliance
Mess-Sender: <servicename>
Mess-Request-Id: <uuid4>
Mess-Timestamp: <iso8601>
Content-Type: JSON | XML | TXT
Content-Length: <int>
Mess-Body-Hash: sha256=<hex>
Mess-Encrypted: aes-256-gcm        (solo per Request cifrate)
Mess-Nonce: <base64>               (solo per Request cifrate)

<body>      # ciphertext+tag se Mess-Encrypted, altrimenti plaintext
```

### Compliance verso il Collector

Due fasi distinte (decise dal campo `phase` nel body):

**Pre-Compliance** (sender -> collector, prima di mandare al receiver):
```json
{
  "phase": "pre",
  "key": "<hex>",
  "nonce": "<hex>",
  "request_metadata": { ...header del Mess Request originale... }
}
```

**Post-Compliance** (receiver -> collector, dopo aver risposto al sender):
```json
{
  "phase": "post",
  "request_metadata":  { ... },
  "response_metadata": { ... }
}
```

Validazione di ogni messaggio in arrivo:
- **Content-Length**: byte ricevuti = dichiarati.
- **Mess-Body-Hash**: SHA-256 del body = dichiarato.
- **AES-GCM tag** (per i ciphertext): autenticato con AAD = `request_id`.

## Layout del codice

```
mess/
  protocol/
    models.py        # Mess, MessEvent, ContentType, factory compliance
    message.py       # encode/decode payload (JSON/XML/TXT)
    socket_io.py     # framing: read_message / write_message
    crypto.py        # wrapper AES-256-GCM
    pool.py          # ConnectionPool keep-alive
    exceptions.py    # MessError + sottoclassi
    sender.py        # MessSender: client TCP, two-phase send
    receiver.py      # MessReceiver: server TCP, key-fetch + post-compliance async
    collector.py     # ComplianceCollector: state machine in-memory
  helpers/
    quick.py         # send / serve / run_collector / decode (one-call)
    service.py       # Service: config persistente + from_env

examples/             # esempi end-to-end self-contained
  run_collector.py     # collector standalone (companion dei due sotto)
  echo_server.py       # server in un processo dedicato
  echo_client.py       # client che parla col server
  with_service.py      # tutti i 3 ruoli in un solo processo via Service
  compliance_failure.py # demo del fail-stop quando il collector e giu

docs/
  presentation.md / .pdf
  sequence.excalidraw
```

## Eccezioni

Tutto eredita da `MessError`:

| Eccezione                  | Quando viene sollevata                                       |
| -------------------------- | ------------------------------------------------------------ |
| `MessProtocolError`        | Versione, separator o header malformati.                     |
| `MessIntegrityError`       | Content-Length o body-hash non corrispondono.                |
| `MessEncodingError`        | Encoding o decoding del payload fallito.                     |
| `MessTransportError`       | Errore socket: connessione chiusa, timeout, refused.         |
| `MessComplianceError`      | Pre-compliance verso il Collector fallita (sender-side).     |
| `MessCryptoError`          | Cifratura/decifratura fallita (incluso tag GCM non valido).  |
| `MessKeyUnavailableError`  | Collector non ha (ancora) la chiave per il request_id.       |

## Quickstart

Avvia un Collector standalone:

```python
from mess.helpers import run_collector
run_collector(port=9001)
```

Server:

```python
from mess.helpers import serve

def handler(body):
    return {"echo": body}

serve(port=9000, handler=handler, name="echo-svc",
      collector=("127.0.0.1", 9001))
```

Client:

```python
from mess.helpers import send, decode

response = send("127.0.0.1", 9000, {"hello": "world"},
                sender="my-client",
                collector=("127.0.0.1", 9001))
print(decode(response))
```

## Service: configurazione persistente + keep-alive

`Service` raggruppa identita, indirizzo di bind e Collector. Il sender
interno mantiene un pool di connessioni TCP riusate tra `.send()`
consecutive verso lo stesso target.

```python
from mess.helpers import Service

svc = Service(
    name="echo-svc",
    host="0.0.0.0", port=9100,
    collector=("collector", 9101),
)

svc.serve(my_handler, block=False)
svc.send("other-svc", 9100, {"msg": "ciao"})
```

Speedup misurato in loopback: con keep-alive 10 request usano 2 socket
totali (1 verso receiver + 1 verso collector) contro ~20 in modalita
one-shot, con un guadagno di latenza ~3x.

## Configurazione da environment (Docker)

`Service.from_env(prefix="MESS")` legge la config da variabili
d'ambiente, pattern naturale per servizi in Docker.

| Variabile          | Obbligatoria | Descrizione                                       |
| ------------------ | ------------ | ------------------------------------------------- |
| `MESS_NAME`        | sì           | Identita del servizio (sender/service_name).      |
| `MESS_COLLECTOR`   | sì           | Collector nel formato `host:port`.                |
| `MESS_HOST`        | no           | Bind host del receiver. Es. `0.0.0.0`.            |
| `MESS_PORT`        | no           | Bind port del receiver.                           |
| `MESS_TIMEOUT`     | no           | Timeout sender in secondi (float, default 5).     |

Esempio `docker-compose.yml`:

```yaml
services:
  echo:
    environment:
      MESS_NAME: echo-svc
      MESS_HOST: 0.0.0.0
      MESS_PORT: 9100
      MESS_COLLECTOR: collector:9101
  collector:
    environment:
      MESS_NAME: compliance-collector
      MESS_HOST: 0.0.0.0
      MESS_PORT: 9101
```

Nel codice:

```python
from mess.helpers import Service
svc = Service.from_env()
svc.serve(my_handler)
```

## Garanzie del modello encryption-gated

- **Audit non aggirabile**: nessuna richiesta puo essere processata se la
  Compliance Pre non e stata registrata. Il Receiver, anche bacato o
  malevolo, non puo "saltare" la registrazione.
- **Confidenzialita in transito**: il body Request viaggia cifrato
  AES-256-GCM. Mess-Sender e Mess-Request-Id rimangono in chiaro (servono
  per il routing).
- **Tamper evidence**: AAD = `request_id`, qualsiasi alterazione di
  ciphertext o request-id invalida il tag GCM -> `MessCryptoError`.
- **Per-message key**: chiave random 256-bit per ogni messaggio. La
  compromissione di una chiave non si propaga.
- **Fail-stop sul collector down**: se il Collector e irraggiungibile,
  `MessComplianceError` immediato lato sender; nulla viene inviato al
  receiver; il messaggio non esiste, fine.

## Stato

Versione `0.1.0`. Implementati: protocollo, framing, sender, receiver,
collector con state machine, helpers, configurazione da env, keep-alive,
encryption-gating AES-256-GCM. Da fare: TLS sui socket, retry/backoff
sul Collector lato sender, persistenza al Collector, test automatizzati.

