docs/specs/fapp-security.md: - Full threat model for patient protection - 3-level verification roadmap (transparency → endorsements → registry) - UI warning mockups - Technical implementation plan - Honest assessment of limitations SlotAnnounce changes: - Added profile_url field for therapist verification - New with_profile() constructor - profile_url included in signature docs/specs/fapp-protocol.md: - Added Security & Anti-Fraud section - Link to full security spec
180 lines
7.8 KiB
Markdown
180 lines
7.8 KiB
Markdown
# 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
|
||
|
||
## Security & Anti-Fraud
|
||
|
||
> **See [fapp-security.md](fapp-security.md) for the full security model.**
|
||
|
||
### Patient Protection
|
||
|
||
Patients are vulnerable. FAPP must protect against fraudulent "therapists":
|
||
|
||
| Threat | Mitigation |
|
||
|--------|------------|
|
||
| Fake Therapist | `profile_url` for cross-verification, UI warnings |
|
||
| Impersonation | Ed25519 signatures, endorsement system (planned) |
|
||
| Data Harvesting | Anonymous queries, no patient identity in protocol |
|
||
| Financial Fraud | "Never pay upfront" warnings, reputation (planned) |
|
||
|
||
### Verification Levels
|
||
|
||
| Level | Mechanism | Trust |
|
||
|-------|-----------|-------|
|
||
| 0 | None — only mesh signature | Low |
|
||
| 1 | Endorsement by trusted relay | Medium |
|
||
| 2 | Registry verification (KBV) | High |
|
||
|
||
**Current implementation:** Level 0 with `profile_url` for transparency.
|
||
|
||
### Anti-Spam
|
||
|
||
1. **Approbation hash binding.** The `approbation_hash` field contains SHA-256 of the therapist's Approbation number. Creates accountability — therapist identity tied to real credential.
|
||
2. **Signature verification.** All SlotAnnounces are Ed25519-signed. Relay nodes reject unsigned or invalid announcements.
|
||
3. **Rate limiting.** Relay nodes enforce max 10 SlotAnnounces per hour per therapist_address.
|
||
4. **Sequence-based dedup.** Monotonic counter; relays only accept sequence >= last seen.
|
||
5. **TTL enforcement.** Expired announcements are garbage collected. Default 7 days.
|
||
6. **Hop limit.** max_hops field (default 8) prevents 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.
|