Files
quicproquo/docs/specs/fapp-protocol.md

7.2 KiB
Raw Blame History

FAPP — Free Appointment Propagation Protocol

Purpose

Decentralized psychotherapy appointment discovery over the QuicProQuo mesh network.

In Germany, finding a psychotherapist takes 36 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.