mTLS and propagating identity

An encrypted channel proves the server to the client, but it does not tell the server who called; making both sides present certificates gives each a verified workload identity, and that original caller's identity must ride every hop — because no hop shares memory with the one that started the call.

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

  1. Watch
  2. Try it
  3. Predict
  4. Capture
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
What to watch for

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

Highlighted lines are the ones running in the diagram right now.

Channel.establish
the TLS handshake — and whether the client also proves itself
def establish(server_addr):
conn = tls_handshake(server_addr)
# server's cert always verified by the client
conn.server_id = verify_cert(conn.server_cert)
if mutual_tls:
# client ALSO presents a cert: both sides named
present_cert(conn, self.cert) # e.g. SPIFFE/SVID
conn.client_id = self.workload_identity
else:
conn.client_id = None # caller is just a peer IP
return conn
Client.call
what rides in the call's metadata: a token, and the origin
def call(conn, method, req, ctx):
md = {}
md['authorization'] = mint_token() # per-call badge
if propagate_identity:
# carry who STARTED the call, like grpc-timeout does
md['origin-principal'] = ctx.origin or self.id
# HTTP/2 HEADERS frame carries the metadata
return conn.invoke(method, req, metadata=md)
Server.authorize
the leaf decides on who called — origin or just the last hop
def authorize(conn, md, action):
caller = conn.client_id # verified by mTLS
origin = md.get('origin-principal')
if origin is not None:
# authorize on who STARTED the call
return policy.allow(origin, action)
# no origin forwarded: fall back to immediate caller
return policy.allow(caller, action)

Where this sits in Build a gRPC-style RPC framework

Scene 11 of 14, in the Secure & ship act — mTLS identity, then configure the whole stack.. 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.

Up next. Codec, transport, deadlines, retries, balancing, security — every layer was a forced choice with a trade-off; the last move is to put them all on one canvas and pick a coherent posture for a real workload, defending each choice with the scene that taught it.

All 14 scenes in Build a gRPC-style RPC framework · Every curriculum

Built with Arqly
Every scene in Build a gRPC-style RPC framework builds on the one before it.All 14 Build a gRPC-style RPC framework scenes