feat:added webrtc protocol - #119
Conversation
Signed-off-by: Vinay Singh <vinay@verid.id>
|
I was trying to create another implementation in my own app over the weekend, but I was struggling to understand the message/state flow. Would you be able to add a flow diagram? I may be wrong, but it seems to me like both propose and offer are sent at the same time from the caller. This didn't make much sense to me. On a side note, is there a reason why we're using reject instead of problem-report? The reason in the example seems more like a "problem-report-like" reason. |
|
@TheTechmage You are correct - Both The intended flow is: Why BOTH messages exist:
How the callee communicates acceptance:There is NO separate "accept" message! The acceptance is communicated by:
State transitions: 2. Should We Use
|
| Scenario | Use | Reason |
|---|---|---|
| User clicks "Decline" | reject |
User choice, not an error |
Codec mismatch detected in propose |
reject |
Proactive incompatibility check |
| ICE candidates fail to connect | problem-report |
Runtime connection error |
| DTLS fingerprint doesn't match DID signature | problem-report |
Security verification failure |
| TURN server returns 401 Unauthorized | problem-report |
Infrastructure error |
3. How Acceptance is Communicated
The Complete Flow with Acceptance Signal
Alice (Caller) Bob (Callee)
│ │
│ STATE: IDLE │ STATE: IDLE
│ │
│ [1. Alice creates RTCPeerConnection] │
│ [2. Alice calls createOffer()] │
│ [3. Alice signs fingerprint proof] │
│ │
│─────── propose ──────────────────────────────→ │
│ { media: ["audio","video"], ... } │
│ │
│ STATE: PROPOSING │ [Bob's phone rings]
│ │ [UI shows: "Alice calling..."]
│ │ [Accept] [Decline] buttons
│ │
│─────── offer ────────────────────────────────→ │
│ { sdp: "...", fingerprint_proof: ... } │
│ │
│ STATE: OFFERING │
│ │
│ [Alice waits for either answer or reject...] │
│ │
│ │ === USER DECISION ===
│ │
│ ┌─────────────────────────────────────────────┤
│ │ IF USER CLICKS "ACCEPT": │
│ │ │
│ │ │ [Bob verifies fingerprint_proof]
│ │ │ [Bob creates RTCPeerConnection]
│ │ │ [Bob setRemoteDescription(Alice's offer)]
│ │ │ [Bob creates answer]
│ │ │ [Bob signs his fingerprint]
│ │ │
│←│─────── answer ──────────────────────────── │ ✅ ACCEPTANCE!
│ │ { sdp: "...", fingerprint_proof } │
│ │ │
│ │ STATE: CONNECTING │ STATE: CONNECTING
│ │ │
│ │ [Alice: "Got answer = Bob accepted!"] │
│ │ [Alice setRemoteDescription(Bob's answer)] │
│ │ │
│ │←─────── ice ──────────────────────────────→ │
│ │ │
│ │ STATE: CONNECTED │ STATE: CONNECTED
│ │ [Media flows!] │
│ └─────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────┤
│ │ IF USER CLICKS "DECLINE": │
│ │ │
│←│─────── reject ──────────────────────────── │ ❌ REJECTION!
│ │ { reason: "user-declined" } │
│ │ │
│ │ STATE: CLOSED │ STATE: CLOSED
│ │ [Alice: "Call rejected by Bob"] │
│ │ [Alice closes RTCPeerConnection] │
│ └─────────────────────────────────────────────┘
Key Points:
-
Alice sends
proposeANDofferback-to-back (optimistic connection setup)- Alice creates the PeerConnection BEFORE getting acceptance
- This reduces latency for successful calls
-
Bob receives both messages while the UI is showing "Incoming call..."
-
Bob's response determines acceptance:
- ✅
answermessage = "Yes, I accept" (implicit acceptance) - ❌
rejectmessage = "No, I decline" (explicit rejection)
- ✅
-
Alice knows Bob accepted when she receives
answer- State transition:
OFFERING→answer received→CONNECTING
- State transition:
-
If Bob rejects, Alice gets
rejectinstead- State transition:
*→reject received→CLOSED
- State transition:
Why This Design?
This is standard WebRTC pattern (called "offer-answer model" from RFC 8829 JSEP):
- Reduces Latency: Alice can start ICE candidate gathering immediately https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidate_event
- Simpler State Machine: Only 2 possible responses (
answerorreject) - WebRTC-Native: Maps directly to
createOffer()→createAnswer()flow
| Alice Sends | Bob's Options | What Alice Receives | Meaning |
|---|---|---|---|
propose + offer |
User accepts | answer (with SDP) |
✅ "Yes, let's connect!" |
propose + offer |
User declines | reject (with reason) |
❌ "No, I can't/won't" |
propose + offer |
Timeout | (nothing) | ⏱️ "Bob is offline/busy" |
|
@TheTechmage we can move |
|
Just a thought I had, since propose and offer are generally sent at the same time, I retract my question. The use case for having them separate is related to SFU connections. So a user sends a proposal for an SFU-based chat, then an SFU invitation is forwarded in place of an offer. Is that correct? Would love to see the flow charts and the full DIDComm messages added to the document! |
|
@TheTechmage will update the flow chart in the readme and make it more readable. I'll also have a credo-ts package with all the messages implemented it'll be clear then. |
|
@TheTechmage the implementation is here now |
|
As discussed today, this has been in review for way too long in my opinion and it's in the proposed state. We're merging this and any updates can be handled in future PRs |
Add WebRTC 1.0 Protocol
Summary
This PR introduces the WebRTC 1.0 protocol that uses DIDComm v2 as the signaling channel for secure, privacy-preserving audio/video calls. It supports both peer-to-peer mesh (2–8 participants) and SFU‑mediated (10–100+) topologies in a single, cohesive spec. The protocol binds WebRTC DTLS certificate fingerprints to DID keys to prevent MITM, preserves E2EE in SFU mode via SFrame, and adds Out‑of‑Band (OOB) one‑click join links (QR/URL) with ephemeral prekeys for a smooth onboarding experience.
What's Added
https://didcomm.org/webrtc/1.0Key Features
Topologies
propose,offer,answer,ice,renegotiate,update,endsfu-mediatorrole; messagescreate-room,join-room,room-offer,room-answer,participant-joined,participant-left,quality-update,simulcast-control,leave-room,destroy-roomSignaling & Identity
End-to-End Encryption
Out-of-Band Join Links (OOB)
Privacy & Network
Protocol Messages (selection)
Mesh
propose,offer,answer,ice,renegotiate,update,reject,endSFU
create-room,room-created,invite,join-room,room-offer,room-answer,participant-joined,participant-left,quality-update,simulcast-control,leave-room,destroy-room,ping/pongState Machine
IDLE→PROPOSING/CREATING_ROOM→OFFERING/ROOM_ACTIVE→JOINING→CONNECTING→CONNECTED→CLOSED, with glare handling and ICE restart support.Use Cases
Security Highlights
use_limit), audience/origin binding, and per‑link ephemeral prekeysInterop & Composition
References