feat: FAPP — Free Appointment Propagation Protocol for psychotherapy discovery
This commit is contained in:
1079
crates/quicprochat-p2p/src/fapp.rs
Normal file
1079
crates/quicprochat-p2p/src/fapp.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@
|
||||
pub mod address;
|
||||
pub mod announce;
|
||||
pub mod announce_protocol;
|
||||
pub mod fapp;
|
||||
pub mod broadcast;
|
||||
pub mod envelope;
|
||||
pub mod envelope_v2;
|
||||
|
||||
154
docs/specs/fapp-protocol.md
Normal file
154
docs/specs/fapp-protocol.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# FAPP — Free Appointment Propagation Protocol
|
||||
|
||||
## Purpose
|
||||
|
||||
Decentralized psychotherapy appointment discovery over the QuicProQuo mesh network.
|
||||
|
||||
In Germany, finding a psychotherapist takes 3–6 months. The KV (Kassenärztliche Vereinigung) system artificially limits slot visibility. FAPP enables licensed therapists to directly announce free appointment slots into a decentralized mesh, where patients can discover and reserve them without a central registry.
|
||||
|
||||
## Privacy Model
|
||||
|
||||
- **Therapist identity is public.** Therapists are licensed professionals (Approbation). Their mesh identity is linked to a hashed Approbationsurkunde number. This is intentional — patients need to verify they are booking with a real therapist.
|
||||
- **Patient queries are anonymous.** Patients never reveal their identity when searching. SlotQuery messages carry no identifying information. Only when a patient decides to reserve a slot do they establish an E2E-encrypted channel to the therapist — and even then, the mesh sees only encrypted traffic to the therapist's address.
|
||||
|
||||
## Capability Flags
|
||||
|
||||
FAPP extends the announce.rs capability bitfield:
|
||||
|
||||
| Flag | Value | Description |
|
||||
|------|-------|-------------|
|
||||
| `CAP_FAPP_THERAPIST` | `0x0100` | Node is a licensed therapist publishing slots |
|
||||
| `CAP_FAPP_RELAY` | `0x0200` | Node caches and relays FAPP slot announcements |
|
||||
| `CAP_FAPP_PATIENT` | `0x0400` | Node can issue anonymous slot queries |
|
||||
|
||||
## Message Types
|
||||
|
||||
### 1. SlotAnnounce
|
||||
|
||||
Therapist publishes free time slots into the mesh.
|
||||
|
||||
**Fields:**
|
||||
- `id: [u8; 16]` — Unique announcement ID
|
||||
- `therapist_address: [u8; 16]` — MeshAddress of the therapist node
|
||||
- `fachrichtung: Vec<Fachrichtung>` — Therapy specializations offered
|
||||
- `modalitaet: Vec<Modalitaet>` — Session modalities (Praxis, Video, Hybrid)
|
||||
- `kostentraeger: Vec<Kostentraeger>` — Accepted payment/insurance types
|
||||
- `location_hint: String` — PLZ (postal code) only, never exact address
|
||||
- `slots: Vec<TimeSlot>` — Available time slots
|
||||
- `approbation_hash: [u8; 32]` — SHA-256 of the therapist's Approbation number
|
||||
- `sequence: u64` — Monotonically increasing per therapist (dedup/supersede)
|
||||
- `ttl_hours: u16` — Time-to-live in hours (default: 168 = 7 days)
|
||||
- `timestamp: u64` — Unix seconds at creation
|
||||
- `signature: [u8; 64]` — Ed25519 signature over all fields except signature and hop_count
|
||||
- `hop_count: u8` — Current propagation hop count
|
||||
- `max_hops: u8` — Maximum propagation hops
|
||||
|
||||
**Propagation:** Like MeshAnnounce — flooded to neighbors, deduped by `(therapist_address, sequence)`. Higher sequence supersedes lower. Expired announcements (timestamp + ttl_hours exceeded) are dropped.
|
||||
|
||||
### 2. SlotQuery
|
||||
|
||||
Patient requests available slots matching criteria. Anonymous — no patient identity attached.
|
||||
|
||||
**Fields:**
|
||||
- `query_id: [u8; 16]` — Random query identifier for response correlation
|
||||
- `fachrichtung: Option<Fachrichtung>` — Filter by specialization
|
||||
- `modalitaet: Option<Modalitaet>` — Filter by modality
|
||||
- `kostentraeger: Option<Kostentraeger>` — Filter by insurance type
|
||||
- `plz_prefix: Option<String>` — Filter by PLZ prefix (e.g. "80" for München area)
|
||||
- `earliest: Option<u64>` — Earliest acceptable slot (Unix seconds)
|
||||
- `latest: Option<u64>` — Latest acceptable slot (Unix seconds)
|
||||
- `slot_type: Option<SlotType>` — Filter by appointment type
|
||||
- `max_results: u8` — Maximum number of results requested
|
||||
- `hop_count: u8` — Current hop count
|
||||
- `max_hops: u8` — Maximum query propagation hops
|
||||
- `return_path: Vec<[u8; 16]>` — Onion-style return path (mesh addresses)
|
||||
|
||||
**Propagation:** Forwarded like announces but with shorter TTL. Relay nodes with cached SlotAnnounces can respond directly.
|
||||
|
||||
### 3. SlotResponse
|
||||
|
||||
Matching slots returned to the querier via the return path.
|
||||
|
||||
**Fields:**
|
||||
- `query_id: [u8; 16]` — Correlates to the original SlotQuery
|
||||
- `matches: Vec<SlotAnnounce>` — Matching slot announcements (full, so patient can verify signatures)
|
||||
|
||||
### 4. SlotReserve
|
||||
|
||||
Patient claims a specific slot. E2E encrypted to the therapist.
|
||||
|
||||
**Fields:**
|
||||
- `slot_announce_id: [u8; 16]` — ID of the SlotAnnounce being reserved
|
||||
- `slot_index: u16` — Index into the SlotAnnounce's slots vector
|
||||
- `patient_ephemeral_key: [u8; 32]` — X25519 ephemeral public key for reply encryption
|
||||
- `encrypted_contact: Vec<u8>` — Patient contact info, encrypted to therapist's key
|
||||
|
||||
### 5. SlotConfirm
|
||||
|
||||
Therapist confirms or rejects a reservation.
|
||||
|
||||
**Fields:**
|
||||
- `slot_announce_id: [u8; 16]` — Original SlotAnnounce ID
|
||||
- `slot_index: u16` — Slot index
|
||||
- `confirmed: bool` — Whether the reservation is accepted
|
||||
- `encrypted_details: Vec<u8>` — Appointment details, encrypted to patient's ephemeral key
|
||||
|
||||
## Data Model
|
||||
|
||||
### Fachrichtung (Therapy Specialization)
|
||||
|
||||
| Variant | Description |
|
||||
|---------|-------------|
|
||||
| `Verhaltenstherapie` | Cognitive behavioral therapy (CBT) |
|
||||
| `TiefenpsychologischFundiert` | Psychodynamic therapy |
|
||||
| `Analytisch` | Psychoanalysis |
|
||||
| `Systemisch` | Systemic therapy |
|
||||
| `KinderJugend` | Child and adolescent psychotherapy |
|
||||
|
||||
### Modalitaet (Session Modality)
|
||||
|
||||
| Variant | Description |
|
||||
|---------|-------------|
|
||||
| `Praxis` | In-person at the therapist's practice |
|
||||
| `Video` | Video session (Videosprechstunde) |
|
||||
| `Hybrid` | Either in-person or video |
|
||||
|
||||
### Kostentraeger (Insurance/Payment)
|
||||
|
||||
| Variant | Description |
|
||||
|---------|-------------|
|
||||
| `GKV` | Gesetzliche Krankenversicherung (statutory health insurance) |
|
||||
| `PKV` | Private Krankenversicherung (private health insurance) |
|
||||
| `Selbstzahler` | Self-pay |
|
||||
|
||||
### SlotType (Appointment Type)
|
||||
|
||||
| Variant | Description |
|
||||
|---------|-------------|
|
||||
| `Erstgespraech` | Psychotherapeutische Sprechstunde (initial consultation) |
|
||||
| `Probatorik` | Probatorische Sitzungen (trial sessions) |
|
||||
| `Therapie` | Regular therapy session |
|
||||
| `Akut` | Akutbehandlung (acute/crisis treatment) |
|
||||
|
||||
### TimeSlot
|
||||
|
||||
- `start_unix: u64` — Start time in Unix seconds
|
||||
- `duration_minutes: u16` — Duration (typically 50 or 25 minutes)
|
||||
- `slot_type: SlotType` — Type of appointment
|
||||
|
||||
## Anti-Spam
|
||||
|
||||
1. **Approbation hash binding.** The `approbation_hash` field contains SHA-256 of the therapist's Approbation number. While mesh nodes cannot verify this against a registry, it creates accountability — a therapist's identity is tied to a real credential.
|
||||
2. **Signature verification.** All SlotAnnounces are Ed25519-signed. Relay nodes reject unsigned or invalid announcements.
|
||||
3. **Rate limiting.** Relay nodes enforce a maximum announcement rate per therapist address (e.g., max 10 SlotAnnounces per hour per therapist_address).
|
||||
4. **Sequence-based dedup.** Each therapist maintains a monotonic sequence counter. Relay nodes only accept announces with sequence >= last seen for that therapist.
|
||||
5. **TTL enforcement.** Expired announcements are garbage collected. Default TTL is 7 days.
|
||||
6. **Hop limit.** SlotAnnounces have a max_hops field (default 8) to prevent infinite propagation.
|
||||
|
||||
## Wire Format
|
||||
|
||||
All FAPP messages use CBOR serialization (ciborium), consistent with MeshEnvelope and MeshAnnounce.
|
||||
|
||||
## No Central Registry
|
||||
|
||||
Slots live exclusively in the mesh. Relay nodes with `CAP_FAPP_RELAY` cache active SlotAnnounces and respond to queries. There is no central database, no API server, no single point of failure. The mesh IS the registry.
|
||||
@@ -1,5 +1,32 @@
|
||||
# Status Log
|
||||
|
||||
## 2026-03-31 — FAPP: Free Appointment Propagation Protocol
|
||||
|
||||
### Completed
|
||||
- **Protocol spec** — `docs/specs/fapp-protocol.md`: decentralized psychotherapy appointment discovery over mesh
|
||||
- **Rust module** — `crates/quicprochat-p2p/src/fapp.rs`: full data structures, store, query matching, signature verification
|
||||
- **Message types**: SlotAnnounce, SlotQuery, SlotResponse, SlotReserve, SlotConfirm
|
||||
- **Domain model**: Fachrichtung, Modalitaet, Kostentraeger, SlotType (German enum names for domain concepts)
|
||||
- **FappStore**: in-memory cache with dedup (therapist_address + sequence), TTL expiry, signature verification, capacity limits
|
||||
- **Query matching**: filter by Fachrichtung, Modalitaet, Kostentraeger, PLZ prefix, time range, SlotType, max_results
|
||||
- **Tests**: 16 inline tests covering creation, signing, verification, tampering, forwarding, expiry, CBOR roundtrip, store dedup, sequence supersede, query filters (PLZ, SlotType, Kostentraeger, max_results)
|
||||
- **Privacy model**: therapist identity public (Approbation-bound), patient queries anonymous
|
||||
|
||||
### Design Decisions
|
||||
- Extends announce.rs capability bitfield with CAP_FAPP_THERAPIST (0x0100), CAP_FAPP_RELAY (0x0200), CAP_FAPP_PATIENT (0x0400)
|
||||
- Uses same signing pattern as MeshAnnounce: hop_count excluded from signature, forwarding nodes don't re-sign
|
||||
- CBOR wire format consistent with existing envelope/announce code
|
||||
- Location hint is PLZ only (e.g. "80331") — never exact address
|
||||
- Anti-spam: Approbation hash binding, signature verification, sequence-based dedup, rate limiting, TTL enforcement
|
||||
|
||||
### What's Next
|
||||
- Integrate FAPP message handling into mesh_router.rs
|
||||
- SlotReserve/SlotConfirm E2E encryption (X25519 key exchange)
|
||||
- Return-path routing for anonymous SlotQuery responses
|
||||
- Rate limiting per therapist address in FappStore
|
||||
|
||||
---
|
||||
|
||||
## 2026-03-30 — Implementation Sprint (S4-S5 + MLS-Lite)
|
||||
|
||||
### Completed
|
||||
|
||||
Reference in New Issue
Block a user