gRPC & HTTPS

JARS DEMO · Hands-on workshop

gRPC & HTTPS

From Requests to Secure RPC.

Understand how HTTP, TLS, HTTP/2, and gRPC work together. Build a real service, secure the connection, and learn to diagnose the failures that matter.

14 chapters · about 3.75 hours · self-paced · runs entirely on your machine

REST in this workshop

  1. JSONself-describing text
  2. HTTP/1.1one request at a time per connection
  3. TLS 1.3port 8443 (8080 without TLS)
  4. TCP

gRPC in this workshop

  1. Protocol Buffersbinary, schema from .proto
  2. HTTP/2streams multiplexed on one connection
  3. TLS 1.3 · ALPN h2port 50051, mutual TLS on 50052
  4. TCP
Same business logic, two protocol stacks. HTTPS is HTTP inside TLS; HTTP/2 is a protocol version, not a synonym for HTTPS. Protocol Buffers encode data - they do not encrypt it.

Learning path

Observe it, break it, fix it

Each step builds on the last. You send real requests, read real bytes and certificates, trigger real failures, and verify the fix.

REST vs gRPC

One service, two front doors

Both interfaces call the same Python functions, so the differences you measure are the protocols’. Neither is universally faster: payload size, concurrency, connection reuse and implementation decide. Chapter 12 shows how to measure honestly.
 REST (FastAPI, HTTP/1.1)gRPC (HTTP/2)
AddressingPOST /v1/greetingsWorkshopService/SayHello
PayloadJSON text, readable without a schemaProtobuf binary, decoded with the .proto
ContractOptional (OpenAPI, generated from code here)Required (.proto, code generated from it)
ErrorsHTTP status + JSON body (404, 422)gRPC status in trailers (NOT_FOUND, INVALID_ARGUMENT)
StreamingNot built inServer, client and bidirectional
BrowsersNativeNeeds gRPC-Web and a proxy
Debuggingcurl, any browserGenerated clients, grpcurl with the .proto

How it works

From handshake to RPC

Rendered from the same Mermaid source as docs/architecture.md.

TLS: the handshake and the trust decision

The client checks four things before sending a single request: the chain leads to a CA it trusts, the hostname is in the certificate’s SAN, the certificate is within its validity period, and the server holds the matching private key.

sequenceDiagram
  participant C as Client (trusts ca.crt)
  participant S as Server (server.crt + server.key)
  C->>S: ClientHello (SNI: localhost, ALPN: h2, key share)
  S-->>C: ServerHello (key share) - keys derived, rest encrypted
  S-->>C: Certificate server.crt, CertificateVerify (signed by server.key)
  Note over C: 1. chain: server.crt signed by ca.crt?<br/>2. hostname: "localhost" in SAN?<br/>3. validity: now within notBefore..notAfter?<br/>4. possession: signature valid?
  C->>S: Finished
  Note over C,S: application data (HTTP/1.1 or HTTP/2) flows encrypted
TLS 1.3 handshake: ClientHello with SNI and ALPN, ServerHello, server certificate and signature, client verification of chain, hostname, validity and key possession, then encrypted application data.

gRPC: a call over HTTP/2

A generated stub serializes the request; the channel sends it as an HTTP/2 stream whose path names the method; the server routes it to your servicer; the status comes back in the trailers.

flowchart TB
  subgraph Client
    A["grpc_client / your code"] --> B["generated stub<br/>WorkshopServiceStub"]
    B --> C["channel<br/>(one reused HTTP/2 connection)"]
  end
  C -- "TLS 1.3 + ALPN h2" --> D
  subgraph "grpc-server"
    D["grpc.server<br/>HTTP/2 streams, max message size"] --> E["StatsInterceptor<br/>count + log method"]
    E --> F["WorkshopServicer / AdminServicer<br/>routed by :path"]
    F --> G["core (domain)"]
  end
  F -. "status in trailers<br/>grpc-status, grpc-message" .-> C
gRPC request flow: client code, generated stub, channel over TLS with ALPN h2, gRPC server, interceptor, servicer routed by path, domain logic, and the status returned in HTTP/2 trailers.

Certificate lab

Your own CA, and every way trust can fail

Generate a local educational CA and certificates with OpenSSL, then break each verification check on purpose and read the real error.

ca.crt

JARS Workshop Local CA

CA:TRUE · trusted by every client and server

server.crt

grpc-server

SAN: localhost, grpc-server, rest-https

client.crt

workshop-client

clientAuth · authenticated, not admin

admin.crt

workshop-admin

clientAuth · authorized for AdminService

expired-server.crt

grpc-expired

valid Jan 1-31, 2024 only

rogue-ca.crt

Rogue Untrusted CA

nobody trusts it

rogue-client.crt

workshop-client

same name as client.crt - rejected

Generated locally by certs/scripts/certs.py with OpenSSL. Never committed, never baked into images, never to be trusted outside the workshop.
Terminal command to run yourself
uv run python certs/scripts/certs.py generate
uv run python certs/scripts/certs.py inspect server
uv run python certs/scripts/certs.py verify
uv run python certs/scripts/certs.py rotate server
  • Untrusted issuerCERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate
  • Hostname not in SANHostname Verification Check failed
  • Expired certificateCERTIFICATE_VERIFY_FAILED: certificate has expired
  • No client certificatePEER_DID_NOT_RETURN_A_CERTIFICATE (server log)
  • Authenticated, not authorizedPERMISSION_DENIED

Curriculum

14 chapters, about 3.75 hours

Every chapter has objectives, exact commands, the output to expect, a common failure to diagnose, and a knowledge check.

Get started

Prerequisites and local setup

Everything runs locally: no cloud account, no API keys. Commands work in PowerShell, bash and zsh. You run them in your terminal - nothing on this site executes on your machine.
ToolVersionFor
Docker Desktop / EngineCompose v2.20+runs the services and this site
uv0.5+Python 3.13, clients, examples, tests
OpenSSL1.1.1 or 3.xcertificates (or use the toolbox container)
Node.js22only to build the website yourself
Terminal command to run yourself
git clone https://github.com/jars-demo/grpc-demo.git
cd grpc-demo
uv sync
uv run python certs/scripts/certs.py generate
uv run python scripts/tasks.py up
uv run python scripts/smoke_test.py

Then your first REST, gRPC and mutual TLS calls:

Terminal command to run yourself
uv run python examples/rest_client.py https://localhost:8443/v1/greetings --name Ada
uv run python -m grpc_client hello --name Ada
uv run python -m grpc_client --target localhost:50052 --cert certs/out/client.crt --key certs/out/client.key hello

Final challenge

Repair six broken secure connections

Each scenario is a real misconfigured client against real servers. Reproduce the failure, read the error, inspect, fix the profile, and verify. The checker also rejects fixes that weaken security.

Scenario 01

The certificate nobody trusts

Scenario 02

Right server, wrong name

Scenario 03

The door that asks for ID

Scenario 04

Same name, wrong signature

Scenario 05

Authenticated is not authorized

Scenario 06

Impatient client

Open source

Contribute

Source code

MIT licensed, on GitHub.

Contribution guide

Setup, checks, and how chapters are tested.

Report an issue

A command that did not work? Tell us.