# JWT -> filter -> TenantContext -> schema-per-tenant (shared pool + search_path), real HTTP provisioned schemas: acme, globex (ghostco deliberately not provisioned) == the filter: who gets in -- no Authorization header GET /invoices -> HTTP 401 {"error":"missing bearer token"} -- garbage token GET /invoices -> HTTP 401 {"error":"invalid token"} -- token signed with a different secret GET /invoices -> HTTP 401 {"error":"invalid token"} -- expired token (exp = 10 minutes ago) GET /invoices -> HTTP 401 {"error":"invalid token"} -- valid signature, no tenant_id claim GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"} -- valid signature, tenant_id = "x; drop schema public cascade" GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"} -- valid signature, tenant_id = "public" (reserved name) GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"} == two tenants, same URL, different data -- acme creates INV-1 POST /invoices -> HTTP 201 {"id":1,"number":"INV-1","amount":100.00,"tenant":"acme"} -- acme creates INV-2 POST /invoices -> HTTP 201 {"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"} -- globex creates INV-9 POST /invoices -> HTTP 201 {"id":1,"number":"INV-9","amount":999.00,"tenant":"globex"} -- acme lists GET /invoices -> HTTP 200 [{"id":1,"number":"INV-1","amount":100.00,"tenant":"acme"},{"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}] -- globex lists GET /invoices -> HTTP 200 [{"id":1,"number":"INV-9","amount":999.00,"tenant":"globex"}] == cross-tenant read by id -- globex asks for acme's INV-2 by id (2) GET /invoices/2 -> HTTP 404 -- acme asks for the same id GET /invoices/2 -> HTTP 200 {"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"} == a well-formed tenant nobody has onboarded -- token for tenant "ghostco": GET /invoices GET /invoices -> HTTP 500 HTTP Status 500 – Internal Server Error