7.2 KiB
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 IDtherapist_address: [u8; 16]— MeshAddress of the therapist nodefachrichtung: Vec<Fachrichtung>— Therapy specializations offeredmodalitaet: Vec<Modalitaet>— Session modalities (Praxis, Video, Hybrid)kostentraeger: Vec<Kostentraeger>— Accepted payment/insurance typeslocation_hint: String— PLZ (postal code) only, never exact addressslots: Vec<TimeSlot>— Available time slotsapprobation_hash: [u8; 32]— SHA-256 of the therapist's Approbation numbersequence: u64— Monotonically increasing per therapist (dedup/supersede)ttl_hours: u16— Time-to-live in hours (default: 168 = 7 days)timestamp: u64— Unix seconds at creationsignature: [u8; 64]— Ed25519 signature over all fields except signature and hop_counthop_count: u8— Current propagation hop countmax_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 correlationfachrichtung: Option<Fachrichtung>— Filter by specializationmodalitaet: Option<Modalitaet>— Filter by modalitykostentraeger: Option<Kostentraeger>— Filter by insurance typeplz_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 typemax_results: u8— Maximum number of results requestedhop_count: u8— Current hop countmax_hops: u8— Maximum query propagation hopsreturn_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 SlotQuerymatches: 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 reservedslot_index: u16— Index into the SlotAnnounce's slots vectorpatient_ephemeral_key: [u8; 32]— X25519 ephemeral public key for reply encryptionencrypted_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 IDslot_index: u16— Slot indexconfirmed: bool— Whether the reservation is acceptedencrypted_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 secondsduration_minutes: u16— Duration (typically 50 or 25 minutes)slot_type: SlotType— Type of appointment
Anti-Spam
- Approbation hash binding. The
approbation_hashfield 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. - Signature verification. All SlotAnnounces are Ed25519-signed. Relay nodes reject unsigned or invalid announcements.
- Rate limiting. Relay nodes enforce a maximum announcement rate per therapist address (e.g., max 10 SlotAnnounces per hour per therapist_address).
- Sequence-based dedup. Each therapist maintains a monotonic sequence counter. Relay nodes only accept announces with sequence >= last seen for that therapist.
- TTL enforcement. Expired announcements are garbage collected. Default TTL is 7 days.
- 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.