Keys, then audio
DTLS, SRTP and one call from cold start, measured.
On this page · 10 sections
Post 3 left Alice with a working path: her laptop’s packets get from 192.0.2.10:54321 to the assistant’s server at 198.51.100.20:50004, and back. So her 120-byte packet from post 1 can travel it. The trouble is that it travels it as it is, and its 80 bytes of Opus are sound, not secret. Any router on the way could hand them to a decoder and listen.
WebRTC doesn’t allow that. Its security architecture says it flatly: “Media traffic MUST NOT be sent over plain (unencrypted) RTP or RTCP” (RFC 8827 §6.5). So before the first 20 ms of sound, two things have to exist that don’t yet: keys that both ends know and nobody else does, and a way to use them on fifty packets a second without slowing any of them down. We’ll see how Alice’s browser and the assistant’s server get both, what encryption actually does to her packet, and, measured, how long the whole call takes to come up from nothing.
The short version
- A call runs on two channels. The signalling channel carries the SDP; the media path carries the handshake and the audio. No key travels on either.
- The SDP carries a fingerprint of each side’s certificate, never a key.
- DTLS is a handshake on the media path: two round trips, after which both sides share a secret nobody watching could work out, and each works out the same audio keys from it, one set per direction.
- SRTP encrypts the payload, leaves the RTP header readable, and adds a 10-byte tag: Alice’s packet goes from 120 bytes to 130.
- Measured, a call needs about three round trips before the first sound: one to check the path, two for the handshake.
Terms we’ll lean on, each explained where it first matters:
| Term | In one line |
|---|---|
| SDP | The text two sides exchange to set up a call: codecs, ICE details, and the fingerprint |
| DTLS | TLS rebuilt to survive UDP’s loss and reordering |
| SRTP | Secure RTP: encryption and an authentication tag on every media packet |
| Fingerprint | A hash of a certificate, sent in the SDP so the other side can recognise the certificate when DTLS shows it |
| Exporter | A DTLS feature that turns a finished handshake into extra key material, here for SRTP |
| Key, salt | What SRTP gets for each direction: the key encrypts, and the salt is a second random value mixed into every packet so no two come out alike |
Two channels, and what each one carries
A WebRTC call has two separate paths, and it matters which one carries what.
The signalling channel is whatever the application chooses. JSEP, the standard for how browsers set up calls, leaves it “entirely up to the application” (RFC 9429 §3.1); a WebSocket over HTTPS to the assistant’s web service is typical. It carries one SDP offer from Alice and one answer back. The media path is the UDP flow ICE found in post 3, and it carries three protocols, starting in this order: the STUN checks, then the DTLS handshake, then SRTP.

The keys are made on the media path, not sent on either. What the signalling channel carries, for security, is only a fingerprint. RFC 5763: “the entire authentication and key exchange for securing the media traffic is handled in the media path through DTLS. The signaling path is only used to verify the peers’ certificate fingerprints” (§5).
Why not just put the key in the SDP? There is a standard for exactly that, SDP security descriptions, and WebRTC forbids it: implementations “MUST NOT offer SDP security descriptions [RFC4568] or select it if offered” (RFC 8827 §6.5). The signalling passes through the application’s servers, its logs and the page’s own JavaScript. A key written there can be read by any of them, quietly, and used to decrypt the call later. A fingerprint written there is useless to someone who only reads it; to attack the call they’d have to rewrite the SDP in transit and then sit on the media path as well, which is a much bigger job than reading a log.
Alice’s offer, line by line
Here’s Alice’s offer, as the demo builds it in the layout of JSEP’s own example, with the candidates, ufrag and SSRC from the earlier posts:
v=0o=- 4611731400430051336 2 IN IP4 0.0.0.0s=-t=0 0a=group:BUNDLE 0a=ice-options:trickle ice2m=audio 50004 UDP/TLS/RTP/SAVPF 111c=IN IP4 192.168.1.42a=mid:0a=sendrecva=rtpmap:111 opus/48000/2a=fmtp:111 minptime=10;useinbandfec=1a=rtcp-fb:111 transport-cca=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-levela=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mida=msid:alice-mic alice-mic-tracka=ssrc:523124044 cname:alicea=ice-ufrag:alcea=ice-pwd:alice+password+23+charsa=fingerprint:sha-256 A9:5C:8D:BA:4C:00:E3:59:80:B8:8E:45:F1:DA:8E:5C:5D:01:AB:C7:32:0C:89:69:94:EE:DC:72:61:A1:00:DDa=setup:actpassa=tls-id:1f2e3d4c5b6a79880a1b2c3d4e5f6071a=rtcp-muxa=rtcp-rsizea=candidate:1 1 udp 2130706431 192.168.1.42 50004 typ hosta=candidate:2 1 udp 1694498815 192.0.2.10 54321 typ srflx raddr 192.168.1.42 rport 50004a=candidate:3 1 udp 16777215 198.51.100.50 49152 typ relay raddr 192.0.2.10 rport 54321a=end-of-candidatesTwenty-eight lines, and most of them are for things earlier posts covered. Grouped by the part of the call that reads them:
| Lines | Read by | What they set |
|---|---|---|
v=, o=, s=, t= | Session bookkeeping | Version 0, an ID, no name, no time limits |
a=ice-ufrag, a=ice-pwd, the candidates, c= | ICE, post 3 | The credentials that sign every check, and every address Alice might be reached at |
a=group:BUNDLE 0, a=rtcp-mux | The transport | Everything on one port: audio, its RTCP, and any later media (RFC 9143, RFC 5761) |
a=rtpmap, a=fmtp | The codec | Opus, payload type 111, with in-band FEC from post 2 |
a=rtcp-fb ... transport-cc | Feedback, post 5 | Per-packet feedback for congestion control |
a=fingerprint, a=setup, a=tls-id | DTLS | Which certificate to expect, and who may start the handshake |
Two things are easy to misread. opus/48000/2 doesn’t mean stereo: RFC 7587 says the clock rate “MUST be 48000, and the number of channels MUST be 2” for every Opus session, and stereo is a separate parameter that defaults to mono (§7). And UDP/TLS/RTP/SAVPF on the m= line is the profile name for DTLS-SRTP, the one trace SRTP leaves in the whole description.
The point to take away is the line that isn’t there: no line carries a key. There’s a fingerprint, a hash of a certificate Alice will show later, and a=setup:actpass, which offers either DTLS role and leaves the choice to the answerer.
The server’s answer is shorter: a=ice-lite, one host candidate, its own fingerprint, and a=setup:active. JSEP says an answerer “SHOULD use the “active” role” when the offer says actpass (RFC 9429 §5.3.1), and RFC 5763 says why: active “allows the answer and the DTLS handshake to occur in parallel”. So the assistant’s server will be the one that starts the handshake.
DTLS borrows TLS’s names for the two roles, and they’re the opposite of what you’d guess here, so it’s worth fixing them now:
| Role | Alice’s browser | The assistant’s server |
|---|---|---|
| DTLS role | DTLS server: waits for the handshake to start | DTLS client: starts it, because its answer said active |
| Sends its audio with | the server_write key | the client_write key |
From here on, “the server” in a sentence about DTLS still means the assistant’s machine, and when the role matters the role is named.
Paste any SDP into the explainer and it sorts every line the same way, says which part of the call reads it, and checks it against the RFCs. The samples include Alice’s offer, the server’s answer, a real stack’s answer, and a broken offer with five mistakes:
The fingerprint is a hash of a certificate. So when does anyone actually see the certificate?
A handshake that survives UDP: DTLS
The certificate shows up in the handshake, on the media path. Here’s the idea before the details. Each side has a certificate: a public key, made fresh for the call, which only its owner can prove it holds. Unlike HTTPS, no certificate authority signs it; what makes it trustworthy is the fingerprint the other side already got in the SDP. The SDP already told each side the other’s fingerprint. In the handshake each side shows its certificate, proves it owns it, and the two of them agree on a shared secret in a way that someone watching every byte still can’t work out. That last trick is ordinary public-key key exchange, the same one HTTPS uses.
What isn’t ordinary is the transport. TLS, the protocol behind HTTPS, assumes TCP’s reliable, ordered delivery, and the media path is UDP, where packets get lost and reordered. RFC 6347: “TLS has no internal facilities to handle this kind of unreliability” (§3). DTLS is TLS with what’s missing added back: it numbers its messages, resends any that don’t get an answer, and splits big ones across several datagrams.
The demo runs a real call between two peers built with aiortc, a Python implementation of WebRTC, records every packet, and decodes the handshake. It’s four bursts of messages, called flights, two round trips in all:

| Flight | Who sends it | What it does |
|---|---|---|
| 1 | The assistant’s server (DTLS client) | “Hello”: the versions and ciphers it can use |
| 2 | Alice (DTLS server) | Her choice of cipher, her certificate, her half of the key exchange, and a request for the other side’s certificate |
| 3 | The assistant’s server | Its certificate, proof that it owns it, its half of the key exchange, and “done” |
| 4 | Alice | ”Done” from her side |
One loopback call, DTLS 1.2: about 2,230 bytes in four packets.
Then each side compares the certificate it was shown with the fingerprint from the SDP. If they don’t match, RFC 5763 says the endpoint “MUST tear down the media session immediately”. The demo tries it: change one character of the answer’s fingerprint and Alice’s side ends in failed and sends nothing.
That makes the fingerprint the whole of the trust. Whoever can rewrite the SDP on its way could swap in their own fingerprint and their own certificate, which is why the signalling channel has to be protected in its own right, with HTTPS.
The handshake in detail: the twelve messages, the missing cookie, and DTLS 1.3
Inside the four flights are twelve handshake messages and two “change cipher” records: ClientHello; then ServerHello, Certificate, ServerKeyExchange, CertificateRequest, ServerHelloDone; then Certificate, ClientKeyExchange, CertificateVerify, ChangeCipherSpec, Finished; then NewSessionTicket, ChangeCipherSpec, Finished. Flight sizes were 173, 727 to 730, 659 and 668 bytes across the runs; the figure’s 730 is from the run with a delay added.
It could have been six flights. RFC 6347 says a DTLS server “SHOULD perform a cookie exchange” by default (§4.2.1), an extra round trip that stops anyone using it to flood a forged address with certificates. This one didn’t.
The demo negotiates DTLS 1.2, because that’s what aiortc’s OpenSSL offers. Chrome added DTLS 1.3 for WebRTC in Chrome 137; before that, its release notes say, “DTLS 1.2 was used for all WebRTC connections” (release notes). Between two Chrome connections it’s 1.3: in Chrome 154 on macOS the lab below reports FEFC, DTLS 1.3.
One port, three protocols
STUN checks, DTLS and the encrypted audio all arrive on the same UDP port, so the receiver needs a way to tell them apart, and it looks at the first byte: 0 to 3 is STUN, 20 to 63 is DTLS, 128 to 191 is audio or its reports (RFC 7983 §7). It works because each protocol’s first byte happens to fall in its own range already.

The demo's classifier
def classify(data: bytes) -> str: first = data[0] if first <= 3: return "STUN" if 20 <= first <= 63: return "DTLS" if 128 <= first <= 191: return "SRTCP" if 192 <= data[1] <= 223 else "SRTP" # RTCP packet types sit where RTP's marker+PT would return "?"The RFC also reserves 16 to 19 for ZRTP and 64 to 79 for TURN channels, which this call doesn’t use; anything outside the ranges is dropped. RTCP shares RTP’s range and is told apart by the second byte, which post 5 picks up.
Where the keys come from
At the end of the handshake, Alice’s browser and the server share a secret that nobody watching could work out. The audio keys come from that secret, and they’re never sent anywhere: each side works them out for itself and gets the same answer.
DTLS can stretch its secret into as many extra bytes as you ask for, a feature called the exporter (RFC 5764 §4.2). WebRTC asks for 60 bytes and cuts them into four pieces:
- a key and a salt for everything the server sends, and
- a key and a salt for everything Alice sends.
The key is what encrypts. The salt is a second random value mixed into every packet’s encryption, so that even two packets with the same content come out differently.

Why a separate key for each direction? Because the encryption SRTP uses works by mixing the audio with a stream of pseudo-random bytes, and the one rule it can’t break is using the same stream twice: RFC 3711 calls that a “two-time pad” and warns it “can seriously compromise security” (§9.1). Alice and the server both send packets numbered from whatever they chose, at the same time, without coordinating. Giving each direction its own key means their streams can never collide, whatever numbers they pick.
The cut in code, and the twelve working keys
cut = { "client_write_SRTP_master_key": material[0:KEY], "server_write_SRTP_master_key": material[KEY : 2 * KEY], "client_write_SRTP_master_salt": material[2 * KEY : 2 * KEY + SALT], "server_write_SRTP_master_salt": material[2 * KEY + SALT :],}Key, key, salt, salt: 16, 16, 14 and 14 bytes, in that order, not key-salt-key-salt. “Client” and “server” here mean the DTLS roles, and Alice is the DTLS server, so she sends with the server_write pair. The demo checks that on a real packet: Alice’s first audio packet decrypts with the server_write half and is rejected with the other. Each pair then goes through SRTP’s own key derivation and becomes six working keys: one to encrypt, one to sign and one salt, for the audio and again for its reports. Twelve in all, from one handshake. (Those sizes are for the profile keys.py asks for; left to choose, two aiortc peers pick an AES-256-GCM profile and export 88 bytes.)
Alice’s packet, encrypted
The demo takes post 1’s packet straight from post 1’s capture, gives SRTP a fixed, fake master key, and protects it with SRTP_AES128_CM_HMAC_SHA1_80, the profile every WebRTC endpoint “MUST” support (RFC 8827 §6.5):
80 6f 03 f2 00 00 25 80 1f 2e 3d 4cExactly as it was. Version 2, payload type 111, sequence 1010, timestamp 9600, SSRC 0x1F2E3D4C. Anyone on the path can still read all of it.62 23 76 62 5a f9 71 28 … 72 moreThe sound, unreadable. All 80 bytes changed; before, they began 4b 41 10 98 fe 7e b5 24. Same length: the encryption adds no padding.a3 29 b9 90 13 a1 c5 52 0a 5dNew. HMAC-SHA1 over the header and the encrypted payload, plus a counter that's never sent, cut to 80 bits.The whole cost on the wire is the tag: 10 bytes a packet.
SRTP has more than one recipe, called a profile, and the two sides agree on one during the handshake. The one shown here is the profile every WebRTC endpoint must support: it encrypts with AES and then signs with a separate 10-byte tag. A newer profile, AES-GCM (RFC 7714), does the encrypting and signing in one step and uses a 16-byte tag, so the same packet would be 108 bytes instead of 102. You don’t need to pick one yourself; the browser does. Chrome 154 on macOS picked the 10-byte one in the lab below, and the lab shows what yours picks.
How does a key turn 80 bytes of sound into 80 bytes of noise? The demo doesn’t take the SRTP library’s word for it. It rebuilds the packet from RFC 3711 alone and gets the same 102 bytes, and the whole recipe is ten lines:
k_e = kdf(master_key, master_salt, 0x00, 16)k_a = kdf(master_key, master_salt, 0x01, 20)k_s = kdf(master_key, master_salt, 0x02, 14)seq, ssrc = struct.unpack("!H", packet[2:4])[0], struct.unpack("!I", packet[8:12])[0]index = (roc << 16) | seq # RFC 3711 section 3.3.1: i = 2^16 * ROC + SEQiv = (int.from_bytes(k_s) << 16) ^ (ssrc << 64) ^ (index << 16) # section 4.1.1header, payload = packet[:12], packet[12:]keystream = aes_cm(k_e, iv.to_bytes(16), len(payload))encrypted = bytes(a ^ b for a, b in zip(payload, keystream))tag = hmac.new(k_a, header + encrypted + struct.pack("!I", roc), hashlib.sha1).digest()[:10] # section 4.2return header + encrypted + tagRead it in four steps:
- Three working keys come out of the key and salt DTLS gave this direction: one to encrypt (
k_e), one to sign (k_a), and a salt (k_s). - A starting point for this packet (
iv) is built from the salt, the stream’s ID and the packet’s number. Every packet gets a different one, so no two packets are ever encrypted the same way. - The payload is mixed with pseudo-random bytes made from the key and that starting point (
keystream), one byte for one byte, so the sound is scrambled and stays 80 bytes long. - The tag is a signature over the header and the scrambled payload, cut to 10 bytes. The header itself is the first 12 bytes in and the first 12 bytes out.
What the receiver refuses
So what stops someone on the path from changing a packet, or sending one again? The demo tries it. A first receiver with the matching keys gets the packet twice; a second, fresh one gets three tampered copies and then the original; a third, holding the other direction’s keys, gets the original too:
| Receiver | Packet | Result |
|---|---|---|
| First | The packet as sent | Accepted, payload restored |
| First | The same packet again | Rejected: replay |
| Second | One bit flipped in the payload | Rejected: authentication failure |
| Second | One bit flipped in the sequence number | Rejected: authentication failure |
| Second | One bit flipped in the tag | Rejected: authentication failure |
| Third, with the other direction’s keys | The packet as sent | Rejected: authentication failure |
| Second | The original, after all those failures | Accepted |
The sequence-number row is the interesting one: the header is readable, but it isn’t changeable, because it’s inside what the tag covers. The replay row is SRTP’s replay list, a window of at least 64 recent packets (§3.3.2): an authentic packet is accepted exactly once. And the last row shows the order of the receiver’s work: three forgeries in a row didn’t change what the second receiver would accept. RFC 3711 has it check for a replay, then check the tag, then decrypt, and only then update its state (§3.3), so a forged packet can’t move anything.

One detail from the code: packet numbers are only 16 bits, so they wrap back to 0 every 65,536 packets, about 22 minutes of Alice talking. Both sides count the wraps themselves, in a rollover counter that’s never sent but goes into every packet’s encryption and tag. The demo sends packet 65535 and then packet 0, and the receiver accepts both, having counted the wrap on its own.
Why the header stays readable
If the header is authenticated anyway, why not encrypt it too? RFC 3711’s reason is plain: “RTP headers are sent in the clear to allow for header compression. This means that data such as payload type, synchronization source identifier, and timestamp are available to an eavesdropper” (§9.4). A later RFC adds that some fields have to stay readable anyway, because “they are used for key scheduling (e.g., RTP synchronization source (SSRC) and sequence number)” (RFC 9335 §1.1): the receiver needs them to know which key and counter to use before it can decrypt anything.
It has a cost a voice post should name. Alice’s offer has an ssrc-audio-level extension: if it’s negotiated, each packet carries one byte saying how loud that 20 ms is, in an 8-byte header extension. (Post 1’s packet was built without extensions to keep its bytes simple, which is why its header is a bare 12 bytes.) It rides in the clear too, and the RFCs call that sensitive, since a loudness trace can give away when someone is speaking. Encrypting header extensions is what RFC 9335’s “cryptex” does, and libwebrtc ships it disabled by default (crypto_options.h).
And it doesn’t mean what it’s often taken to mean. A readable header doesn’t let a media server forward Alice’s audio without her keys. An SFU, a server in the middle of a group call, still decrypts every packet with one leg’s keys and encrypts it again with the next (RFC 7667 §6), which post 6 counts out.
One call from cold start
So how long does all of this take? Picture Alice opening the app for the first time today and saying “hey”. Before the assistant hears a sound, five things have to happen, each waiting for the one before:
- Gather and exchange. Her browser collects its candidates and sends the offer; the answer comes back (post 3, and the start of this one).
- Check. ICE sends its checks and waits for an answer: one round trip.
- Handshake. DTLS sends its four flights: two round trips. It can’t start before a check has worked, because until then there’s no path to send it on (RFC 8842 §6).
- First packet. Only now can her browser encrypt and send audio. It isn’t allowed to send any earlier: WebRTC forbids TLS False Start, a shortcut that would let data start flowing before the handshake has finished (RFC 8827 §6.5).
- First sound. The receiver’s jitter buffer (post 2) waits until it holds a few packets, then plays the first one.
The demo times exactly this between two aiortc peers, 90 times on one machine, and then again with a delay added to every packet to stand in for distance:

| Distance between the two ends | Time to the first sound | Where it went |
|---|---|---|
| None, same machine | 115 ms | About 82 ms is the jitter buffer waiting for four packets, and about 20 ms is the ICE library’s pacing. The protocols themselves took a few milliseconds |
| 50 ms round trip | 255 ms | Plus one round trip for the checks and two for the handshake |
| 100 ms round trip | 404 ms | The same three round trips, twice as long |
Medians of 30 calls each, aiortc 1.15.0 on one Apple M3 Pro.
Two things are worth taking from that. On a fast path, a call’s start-up time is mostly waiting, not cryptography: the handshake’s maths takes milliseconds. On a real path, it’s round trips, about three of them before the first sound, on top of however long the signalling takes. That’s why it matters where the server is: halve the round trip to it and each of those three gets twice as short.
And two tricks claw some of it back. Trickle ICE (post 3) lets the offer go out before gathering is done. And the answer’s setup:active saves a wait. The assistant’s server sends its answer over the signalling channel and, as soon as a check works, its first handshake message over the media path, at the same time. If the roles were the other way round, Alice would have to receive the answer before she could start the handshake, and the two would happen one after the other.
Watch your own browser do it
The lab below sets up a call between two connections in this tab, and asks your browser what it negotiated: the DTLS version, the cipher, the SRTP profile, and which side was the DTLS client. Those are the values I couldn’t pin down for Chrome’s shipping build from its documentation, so here you can read them off your own. It also gives you both SDPs to paste into the explainer.
This lab runs a real call in your browser, so it needs JavaScript.
| Look at | What it shows |
|---|---|
tlsVersion | Four hex digits, the version’s two bytes: FEFD is DTLS 1.2, FEFC is DTLS 1.3 |
srtpCipher | The SRTP profile, by its registry name: the 10-byte-tag profile or a GCM one |
dtlsRole | client on the side that sent the ClientHello, which should be the answerer |
| The two SDPs | No key in either. A fingerprint each, actpass in the offer, active in the answer |
chrome://webrtc-internals shows the same transport stats for any call.
The demo
All the numbers above come from four Python scripts in keys-then-audio/, so you can check them and change them:
| Script | What it does | What to look for |
|---|---|---|
srtp_one_packet.py | Encrypts post 1’s packet with a fixed, fake key, rebuilds the same bytes from the RFC alone, then sends the receiver the seven test packets | The 12-byte header unchanged, the 10-byte tag, and which test packets are refused and why |
keys.py | Runs a real handshake, prints the 60 exported bytes cut into keys and salts, and decrypts Alice’s first real packet with her half | That both sides computed the same 60 bytes, and that the other half fails |
sdp_fixtures.py | Writes the SDP samples the explainer uses, has aiortc parse them, and checks each against the RFCs’ rules | Every sample passing, and the broken offer failing exactly the rules it was built to break |
call_timeline.py | Sets up 30 calls from cold start and times every step; --delay 25 adds 25 ms each way | The same five steps in the same order every time, and where the milliseconds go |
cd mainlog-code/keys-then-audiopython3 -m venv .venv && .venv/bin/pip install -r requirements.txt.venv/bin/python srtp_one_packet.py.venv/bin/python keys.py.venv/bin/python sdp_fixtures.py.venv/bin/python call_timeline.py --delay 25Its CI checks the encrypted bytes and the SDP files exactly, and the call timings only for their order and for a floor under the added delay, since timings depend on the machine.

So before the first 20 ms of Alice’s voice can play, her browser has found a path, agreed on keys over it with a machine it has never met, and wrapped her packet so that every router can deliver it and none can read it. What none of this does is tell Alice’s laptop whether the packets are getting there. The next post is the receiver talking back.
Related posts

Half the bitrate, two thirds the traffic
Halving an audio bitrate from 32 to 16 kb/s took traffic from 48 to 32, because every packet's headers stay the same size. Packet rate is the other control.
Down to the wireaudionetworkingperformance
SFU, MCU, and the participant that has to decode
Put an AI agent in a group call and two questions decide the design: does the model hear each person, or one blend, and who has to hold the keys? Both come down to where the agent's audio gets decoded.
AI & agentic architecturesai-agentswebrtcarchitecture
What a service mesh costs
Your shop now runs 14 services in three languages, and security wants every call between them encrypted and identified. A mesh does that without touching the code, for about a fifth of a millisecond per hop and a tenth of a core per proxy per 1,000 requests a second, measured here with real Envoy. It also brings retry and queue defaults nobody chose. Over plain HTTP they retry less than most people expect; over gRPC, or after one common edit, a failing service gets 27 calls for one checkout.
Down to the wireinfrastructuremicroservicesnetworking
Networking & mediaHalf the bitrate, two thirds the traffic
Halving an audio bitrate from 32 to 16 kb/s took traffic from 48 to 32, because every packet's headers stay the same size. Packet rate is the other control.
audionetworkingAug 2026
AISFU, MCU, and the participant that has to decode
Put an AI agent in a group call and two questions decide the design: does the model hear each person, or one blend, and who has to hold the keys? Both come down to where the agent's audio gets decoded.
ai-agentswebrtcOct 2026
SystemsWhat a service mesh costs
Your shop now runs 14 services in three languages, and security wants every call between them encrypted and identified. A mesh does that without touching the code, for about a fifth of a millisecond per hop and a tenth of a core per proxy per 1,000 requests a second, measured here with real Envoy. It also brings retry and queue defaults nobody chose. Over plain HTTP they retry less than most people expect; over gRPC, or after one common edit, a failing service gets 27 calls for one checkout.
infrastructuremicroservicesOct 2026
Comments