Build a gRPC-style RPC framework (14 scenes)
Scene 11 · mTLS and propagating identity
TLS encrypts and proves the server; mTLS proves both peers with a workload identity. The original caller's identity must propagate across hops, just like a deadline.
Previously

We can now move bytes fast, fairly, and resiliently — but we've never asked who is on the other end of the wire; and because no hop shares memory with the one before it, each hop must carry both a proof of who it is AND the original caller's identity forward, exactly the way it carried the deadline.

Scene 11
mTLS and propagating identity
Diagram
One call drawn as two SEPARATE things — a padlock (the channel is encrypted) and a badge (a per-call token in the metadata) — followed by a 3-hop A→B→C chain. Under plain TLS the server can only see the caller's IP; with mutual certificates both peers display a cryptographically verified workload identity; with identity propagation on, the original caller's identity is forwarded down the chain so the leaf can decide based on who STARTED the call, not just its immediate caller.
SECURITY MODETLSMTLSmTLS+propmTLS — both peers present certs, both identities verifiedENCRYPTED CHANNEL (mutual)PER-CALL TOKEN (metadata)greet("Ada")from prod/frontendclientX.509 CERTprod/frontendverified ✓serverX.509 CERTprod/greeterverified ✓server knows caller: prod/frontendFOUR LEAKSlatencyno shared memorypartial failureconcurrencyBoth peers present certs: each now has a verified workload identity.
padlock = the channel is encrypted
badge = a per-call token (separate thing)
← workload identity: a verified name, both sides
First, separate two ideas people constantly conflate. Encrypting the wire — what HTTPS does — scrambles the bytes so an eavesdropper can't read them, and it proves the SERVER is who it claims (your browser checks the server's certificate). That's the *padlock*. A *per-call token* is a different thing entirely: a credential placed in the call's metadata that says 'this specific call is allowed.' That's the *badge*. The diagram draws them as two distinct objects on purpose. Now the gap: under plain TLS the badge and padlock prove the server to the client, but the server has no proof of who the CLIENT is — it sees only an IP address. When we make *both* sides present a certificate, each peer gets a cryptographically verified name we'll call its **workload identity** — a stable proof like `prod/frontend` of which service this is, not just where it's connecting from. Watch the slider move from TLS to that mutual-cert mode.
Implementation
Channel.establish
the TLS handshake — and whether the client also proves itself
1def establish(server_addr):
2 conn = tls_handshake(server_addr)
3 # server's cert always verified by the client
4 conn.server_id = verify_cert(conn.server_cert)
5 if mutual_tls:
6 # client ALSO presents a cert: both sides named
7 present_cert(conn, self.cert) # e.g. SPIFFE/SVID
8 conn.client_id = self.workload_identity
9 else:
10 conn.client_id = None # caller is just a peer IP
11 return conn
Client.call
what rides in the call's metadata: a token, and the origin
1def call(conn, method, req, ctx):
2 md = {}
3 md['authorization'] = mint_token() # per-call badge
4 if propagate_identity:
5 # carry who STARTED the call, like grpc-timeout does
6 md['origin-principal'] = ctx.origin or self.id
7 # HTTP/2 HEADERS frame carries the metadata
8 return conn.invoke(method, req, metadata=md)
Server.authorize
the leaf decides on who called — origin or just the last hop
1def authorize(conn, md, action):
2 caller = conn.client_id # verified by mTLS
3 origin = md.get('origin-principal')
4 if origin is not None:
5 # authorize on who STARTED the call
6 return policy.allow(origin, action)
7 # no origin forwarded: fall back to immediate caller
8 return policy.allow(caller, action)