Sequel to IAM as Maharaja: How RAJ Redeems the Original Sin of Cloud Authorization
A hotel where the Policy Enforcement Point (PEP) never has to call the Policy Decision Point (PDP)—because that authority is compiled into the keys.
Use a virtual hotel as musical metaphor for how TAJ and RAJEE dematerialize object storage.
ChatGPT Prompt (condensed)
Act I — “This Hotel Is Clearly Fake”
Cast on stage:
- Concierge (Envoy)
- Guests (oblivious, happy)
- Nick McKeown
- Justin Richer
- Torin Sandall (co-creator of Open Policy Agent)
- Charity Majors
- Matt Klein (creator of Envoy)
- Elvis (Mark Miller), in the lounge
- Tom Lehrer, with a slightly battered upright piano on wheels
Scene 1 — The Lobby
Soft ukulele music. Ocean sounds. A sign reads:
TAJ MAHALO — A Luxury Hawaiian Experience
Rooms Across the Islands
A Guest approaches the Concierge.
Guest:
Aloha! I have a reservation. Room 402. Ocean view.
Concierge (pleasant, serene):
Of course. May I see your key?
Guest hands over a glowing TAJ.
The Concierge glances at it briefly, smiles, and gestures vaguely down a hallway that clearly could not contain 400 rooms.
Concierge:
Right this way.
Guest exits happily.
Scene 2 — Suspicion Arrives
Enter the Researchers, carrying clipboards, laptops, and deep unease.
Nick:
This hallway violates topology.
Justin (studying the key):
That key absolutely contains “Room 402.”
But it contains nothing about where Room 402 is.
Torin:
There must be a policy engine somewhere.
Charity:
I just want to know why Room 402 ordered breakfast at 3:12 a.m.
Matt (looking around approvingly):
This is very clean proxy work.
They watch another Guest approach.
Scene 3 — Another Check-In
Guest #2:
Room 318, Garden View, Kauaʻi.
The Concierge checks the TAJ, nods, gestures to the same hallway.
Nick:
Kauaʻi is not down that hallway.
Justin:
That key did not encode geography.
Torin:
I refuse to believe this is happening without evaluation.
🎹 Tom Lehrer Rolls Forward
He taps a key. Sings cheerfully.
🎵
You think you’ve booked a seaside suite,
With towels and mints and something sweet,
But where you are is quite discrete,
And not at all where you suppose…
The hallway bends, the maps all lie,
The islands shift, the guests don’t cry,
And no one asks the reason why,
For every door just… closes. 🎵
He wheels away.
The Researchers stare at him.
Scene 4 — The Investigation (Scooby-Doo Split)
They scatter.
Nick goes “downstairs”
Finds racks of storage crates labeled:
us-west-2 / us-east-1 / ap-southeast-1
Nick:
This is a warehouse.
Justin inspects a discarded key
Justin (holding up the key):
This isn’t a token describing permission.
This is a token describing authority over a room that’s defined somewhere else.
Torin searches for a control room
Opens doors. Finds only more hallway and the ocean.
Torin:
There is no PDP. This is deeply upsetting.
Charity inspects logs on a tablet
Charity:
Every event makes perfect sense.
That’s what bothers me.
Matt leans on a wall
Matt:
This is exactly what a proxy should do.
Scene 5 — Elvis in the Bar
Lights dim to the lounge.
Elvis sits with a guitar.
🎶 Soft strumming.
Elvis:
You don’t need policy if you’ve got the key, baby.
Researchers ignore him.
Scene 6 — The Impossible Map
They gather around a map of the hotel.
Rooms appear on different islands.
Hallways connect nowhere.
Nick flips the map upside down.
Nick:
This is geographically impossible.
Justin:
These keys don’t describe locations.
Torin:
There must be hidden evaluation.
Charity:
Guests are still only entering the rooms they’re supposed to.
Silence.
They all look toward the hallway where Guests continue happily walking.
Closing Beat of Act I
Another Guest approaches.
Guest #3:
Room 402 again. Ocean view.
Concierge checks TAJ. Gestures to the hallway.
Guest exits.
The Researchers stare in horror.
Nick:
That room cannot exist twice.
Justin:
That key did not change.
Torin:
This is impossible without policy.
From the lounge:
Elvis (softly):
Or maybe… the key is the policy.
Blackout.
Act II — “Oh. The Keys Are The Authority.”
Lights up where Act I left off.
The Researchers stand frozen, staring at the hallway that cannot possibly contain the rooms it serves.
Guests continue to stroll past them, cheerful, towel-wrapped, utterly unconcerned with metaphysics.
Scene 1 — The Confrontation
They surround the Concierge.
Nick:
This building is topologically impossible.
Justin:
These keys do not describe permissions.
Torin:
There is no policy engine.
Charity:
And yet nothing incorrect has happened.
Matt:
Which is… impressive.
The Concierge says nothing. Simply holds up a TAJ.
Silence.
They all stare at it.
🎹 Tom Lehrer Slides In
🎵
You searched for rules in cupboards deep,
For policy in hidden keep,
But all along the guests just sleep,
With tokens in their hands…
No runtime check, no PDP,
Just “is this thing allowed to be?”
And suddenly you start to see…
You’re standing on new sands. 🎵
He wheels away.
Scene 2 — Reconstruction
They begin replaying everything they saw in Act I.
Nick:
Control plane intent… compiled into the key.
Justin (slowly):
The token doesn’t describe where the room is.
It describes which floor plan defines where the room is..
Torin (quietly horrified):
Nothing is being decided.
Nothing is being evaluated.
Only discovering where the room must be, given the floor plan in the key.
Charity:
That’s why the logs make sense.
Matt:
This is the cleanest proxy I’ve ever seen.
They look toward the hallway again.
A Guest waves cheerfully while disappearing into architectural nonsense.
Scene 3 — The Islands
A wall map lights up.
Lines draw from:
- Oʻahu → us-west-2
- Maui → us-east-1
- Kauaʻi → eu-central-1
Nick:
The rooms aren’t even on the same island.
Justin:
The keys don’t encode location.
Torin:
The system doesn’t care.
Charity:
Because the key describes the room. Not the building.
They all turn slowly toward the lounge.
Scene 4 — Elvis Explains Everything Without Explaining Anything
Elvis strums.
Elvis:
You folks been lookin’ for a mind where there ain’t one.
They stare.
Elvis:
You thought the hotel needed to remember who’s allowed where.
He holds up a TAJ.
Elvis:
But the guest is carrying the remembering.
Pause.
Elvis:
Honey, the rooms ain’t where they say they are…
but the keys never lie.
Scene 5 — The Scooby-Doo Reveal
They rush him.
Pull off the wig.
It’s still Mark Miller.
They freeze.
He smiles.
Scene 6 — The Realization
Nick:
This is Software-Defined Authorization.
Justin:
This is tokens as artifacts.
Torin:
This is policy removed from the data plane.
Charity:
This is debuggable.
Matt:
This is what Envoy was meant to do.
They all look back at the lobby.
Guests continue checking in, blissfully.
Finale — Tom Lehrer
🎵
For every room there is a key,
No need to ask “may this one be?”
The walls are fake, the guests are free,
And no one needs to roam…
The islands shift beneath your feet,
The storage racks replace the suite,
But nothing breaks and nothing cheats,
Because the key’s the home. 🎵
Closing Line
Charity watches a Guest walk into “Room 402.”
Charity:
I have no idea where that person is.
Beat.
Charity:
But I know the key already proved they’re allowed to be there.
Lights out.
Appendix I — Virtualizing S3: TAJ, RAJ, RAJA, RAJEE
This appendix describes the actual architecture behind TAJ Mahalo seen in the scene.
The hotel is a metaphor, but the system is precise.
This is how you build a logical S3 bucket that:
- hides physical layout
- spans regions
- supports arbitrary collections of objects
- requires no PDP at request time
- and does not give clients bucket access
The four pieces work together:
RAJ — Resource Access JWT (how access is given)
TAJ — Translated Access JWT (how translation is added)
RAJA — the compiler that mints RAJs
RAJEE — the Envoy enforcement engine
I.1 The Problem: Logical ≠ Physical
Clients want to access:
- a logical file:
intro.mp4 - in the collection:
my-project/videos - in the registry:
my-media-prod
But the actual object lives in: s3://prod-us-west-2-media/proj_8473/vod/intro.mp4
Which is a bucket they don’t know about — and are (in general) not allowed to read from!
So instead, they form it into something that looks exactly like an S3 object:
- bucket:
my-media-prod - key:
my-project/videos/intro.mp4
But this is purely logical. It is not a real bucket or key.
The client should never need to know:
- Which bucket
- Which region
- Which prefix structure
- What the real object key is
Traditional solutions:
- API Gateway + Lambda — runtime translation, slow, costly
- S3 Bucket Policy — requires clients know bucket names
- IAM Roles — grants bucket-level access (see IAM as Maharaja)
None of these work for arbitrary logical collections that span physical boundaries.
I.2 The RAJ Solution
The original RAJ (Resource Access JWT) solved the access problem by granting precise access to only that physical key:
{ "iss": "raja.example.com", "sub": "user@example.com", "aud": "rajee.example.com", "exp": 1706227200, "resources": [ { "bucket":"prod-us-west-2-media" "key": "proj_8473/vod/intro.mp4", "actions": ["s3:GetObject"] } ]}
This is compiled authority.
The RAJ contains:
- Expiration
- Physical path (where it actually is)
- Allowed actions
- Cryptographic signature
This provides a secure way to grant limited access to a known resource. But, it requires the client to actually know the final resource. TAJ exists to remove that requirement.
I.3 The TAJ Extension
Under TAJ (Translated Access JWT) — a special kind of RAJ — the client instead requests (and receives) access to what appears to be a normal S3 bucket and key, but is actually a logical key within a specific collection:
{ "iss": "api.example.com", "sub": "user@example.com", "aud": "api.example.com", "exp": 1706227200, "resources": [ { "registry":"my-media-prod", "package":"my-project/videos@abcd1234567890" "path": "intro.mp4", "actions": ["s3:GetObject"] } ]}
The TAJ contains:
- Expiration
- Logical path (how it is known)
- Allowed actions
- Cryptographic signature
This is the “hotel key” metaphor from the play.
I.4 RAJA: The Compiler
RAJA is the control plane service that mints RAJs (of which TAJ is a special case).
When a client requests access:
POST /taj/issue
{ “subject”: “user@example.com”, “bucket”: “my-media-prod”, “path”: “my-project/videos/intro.mp4”, “actions”: [“s3:GetObject”] }
RAJA:
- Pins the package name to a specific revision (the latest hash)
- Evaluates where the subject can access that logical path (this is the only Policy Decision Point in the system)
- Compiles a TAJ with that logical grant
- Returns the TAJ to the client
The heavy lifting happens once, at issuance time.
I.5 RAJEE: The Enforcer
RAJEE is an Envoy filter that:
- Receives requests with TAJ in
Authorization: Bearer <taj> - Decrypts the JWT
- Validates signature and expiry
- Extracts package name and logical key from the purported S3 key
- Retrieves that specific package manifest (from S3 or a cache)
- Translates logical key → physical key
- FAILS closed if it is not found
- Rewrites the S3 request to physical coordinates
- Because AWS SigV4 is region-scoped, RAJEE must also resolve the destination bucket’s region (bucket → region) before it can choose the correct S3 endpoint and sign the request.
- Signs the request with AWS SigV4
- Forwards to the actual bucket
No policy evaluation. No database lookup. Just cryptographic validation and membership checks.
I.6 Example Flow
I.6 Example Flow
Step 1: Client requests access using S3-shaped logical coordinates (unpinned)
POST /taj/issue{ "subject": "user@example.com", "bucket": "my-media-prod", "path": "my-project/videos/intro.mp4", "actions": ["s3:GetObject"]}
Step 2: RAJA issues a TAJ (a special-case RAJ)
{ "iss": "api.example.com", "sub": "user@example.com", "aud": "api.example.com", "exp": 1706227200, "resources": [ { "registry": "my-media-prod", "package": "my-project/videos@abcd1234567890", "path": "intro.mp4", "actions": ["s3:GetObject"] } ]}
Step 3: Client uses the TAJ
GET /my-project/videos/intro.mp4Host: rajee-endpoint.aws.amazon.comAuthorization: Bearer <TAJ>
Step 4: RAJEE rewrites the request (no PDP)
RAJEE performs:
- Validate TAJ signature and expiry
- Confirm the requested logical key is within the TAJ grant
- Fetch the pinned manifest revision:
my-project/videos@abcd1234567890 - Look up
intro.mp4in that manifest - Discover the physical destination:
s3://prod-us-west-2-media/proj_8473/vod/intro.mp4 - Identify the destination bucket’s region for
prod-us-west-2-media(bucket → region),- typically via cached lookup or HeadBucket,
- because SigV4 signing requires the correct region.
RAJEE then rewrites the request to the only place it can possibly go:
GET /proj_8473/vod/intro.mp4Host: prod-us-west-2-media.s3.us-west-2.amazonaws.com
At this point, no decisions remain.
The TAJ already proved:
- who the caller is
- what logical object they are allowed to access
- which manifest revision defines that object
- which physical destination that manifest implies
RAJEE is not choosing a destination.
It is discovering the one that was pre-ordained when RAJA compiled the TAJ.
RAJEE then:
- Signs the rewritten request with AWS SigV4
- Forwards it to S3
Step 5: S3 returns the object
Client receives the video. Never needed to know the physical bucket name, region, or key structure.
I.7 Why This Works
No runtime policy evaluation
Authority is compiled into the RAJ at issuance time.
Physical isolation
Clients never learn bucket names, regions, or structure.
Logical collections
A single TAJ can grant access to objects across multiple buckets.
Auditability
Every request logs the RAJ subject and logical path.
Revocable
RAJs have short TTLs. Refresh requires re-evaluation by RAJA.
Observable
RAJEE logs translations: logical → physical.
I.8 The Hotel Metaphor
| Hotel Metaphor | What the Guest Thinks | What It Actually Is in the System |
|---|---|---|
| The hotel building | A single S3 bucket | A logical collection backed by a manifest |
| Room number (e.g. 402) | An object key | A logical key within the collection |
| “Rooms across the islands” | Objects in one bucket | Objects spread across many buckets and regions |
| The hotel key (TAJ) | Permission to enter a room | A translated RAJ granting access to a logical collection + key |
| The magnetic strip in the key (RAJ) | Encoded room access | Compiled cryptographic authority over specific resources |
| Concierge | Someone checking reservations | RAJEE (Envoy) validating and translating requests |
| Reservation system | Where bookings are stored | RAJA, the control plane compiler and PDP |
| The hallway that makes no sense | Normal S3 routing | Logical → physical translation via manifest lookup |
| The floor plan | Hotel layout | Quilt manifest mapping logical keys to physical destinations |
| “Room 402” appearing twice | A mistake | Same logical key mapped to different physical locations in different versions |
The guests walk through one hallway, but the rooms exist in different warehouses.
The keys never lie, but the building does.
I.9 Comparison to Alternatives
Traditional API Gateway
- ❌ Latency: PDP lookup per request
- ❌ Coupling: API knows all physical locations
- ❌ Scaling: Gateway is a bottleneck
S3 Bucket Policy
- ❌ Clients must know bucket names
- ❌ Cannot span buckets
- ❌ No logical abstraction
IAM Roles + AssumeRole
- ❌ Grants bucket-level access
- ❌ Cannot scope to arbitrary collections
- ❌ Complex to manage
TAJ/RAJ/RAJA/RAJEE
- ✅ No runtime PDP
- ✅ Clients never see physical layout
- ✅ Logical collections span buckets
- ✅ Cryptographically enforced
- ✅ Fully auditable
I.10 Implementation Notes
RAJ Encryption
Use AES-256-GCM with a key known only to RAJA and RAJEE.
RAJ Signing
Use EdDSA (Ed25519) for fast verification.
Path Resolution
RAJA maintains a registry mapping logical → physical.
This can be a database, but only RAJA needs it.
Token Refresh
Short TTLs (5-15 minutes) force re-compilation.
Policy changes take effect on next refresh.
Multi-Region
RAJs can include multiple physical paths for the same logical resource (e.g., replicas).
Rate Limiting
RAJEE can enforce per-subject rate limits without contacting RAJA.
End of Appendix I
Appendix II — Manifest Destinyation
This appendix explains what TAJ is actually virtualizing.
Not prefixes.
Not buckets.
Not paths.
Destinations.
More precisely:
TAJ virtualizes the physical destinations implied by a manifest that has already been bound into authority at issuance time.
This is the part most S3 designs are not built to handle — and why TAJ exists.
II.1 What a Quilt Manifest Is in This System
A Quilt package manifest defines:
- logical keys (what clients request)
- physical S3 destinations (where the data actually lives)
- potentially across buckets and regions
- with no prefix or structural relationship
In TAJ Mahalo terms:
The manifest is the hotel floor plan.
The destinations are where the rooms secretly are.
The room numbers have no resemblance to the warehouse layout.
II.2 TAJ Is Always Manifest-Pinned Authority
Every TAJ is bound to a specific manifest identity (hash/revision) at the moment RAJA issues it.
This means:
- If the manifest changes, existing TAJs do not change meaning
- No floating authority as packages evolve
- No implicit expansion of access
- Temporal skew is explicit and well-defined: old TAJ → old manifest
The manifest is not consulted to decide authorization.
The manifest is consulted because the TAJ already decided authorization.
RAJEE must obtain the exact manifest revision referenced by the TAJ or fail closed.
II.3 Subset Grants Are First-Class
RAJA may mint a TAJ for:
- an entire manifest, or
- a subset of logical keys within a manifest
This is expressed via the path parameter, which may be:
- a single logical object, or
- a logical prefix (
prefix/)
Authority is therefore over logical namespace pinned to a specific manifest revision.
II.4 TAJ Does Not Contain Mappings
TAJ does not embed logical → physical mappings.
Instead, TAJ embeds:
- the identity of the manifest revision that defines those mappings
This keeps TAJ canonical, small, and cryptographically stable.
The manifest remains the authoritative routing table.
Not for policy.
For translation.
II.5 What RAJEE + Lambda Are Actually Doing
At request time, RAJEE:
- Validates the TAJ
- Confirms the requested logical key is within the TAJ’s granted logical scope
- Retrieves the exact manifest revision referenced by the TAJ
- Asks a very narrow question:
“Given this manifest, does this logical key exist, and if so, where is it?”
This Lambda is not an authorizer.
It is a membership prover and destination resolver.
Authorization already happened when RAJA minted the TAJ.
The manifest is used only to prove where the request must go — not whether it is allowed.II.6 One TAJ Per Manifest
A TAJ corresponds to a single manifest grant.
If a user needs access to multiple packages, they hold multiple TAJs.
This keeps tokens simple and avoids cross-manifest complexity.
II.7 Reads Only (for Now)
Scope is currently:
GetObjectonly.
Manifest destinations are about disclosure, not mutation.
II.8 RAJEE May Fetch Manifests on Cache Miss
RAJEE is allowed to fetch the exact manifest revision from Quilt/S3 if not cached.
Because RAJ is manifest-pinned by hash, this is deterministic and safe.
II.9 Why This Works
The same manifest is used:
- once to mint authority (RAJA), and
- again to translate requests (RAJEE)
No policy engine.
No IAM gymnastics.
No large tokens.
Just:
logical room → manifest → physical destination.
II.10 The Insight
Most systems try to virtualize S3 by abstracting paths.
TAJ virtualizes S3 by abstracting destinations implied by manifests.
Hence:
Manifest Destinyation.
End of Appendix II
Appendix III — The Lambda Memberizer
In Appendix II, we saw that RAJEE uses a Lambda during request handling.
This is where many readers instinctively think:
“Ah. Here is the policy engine.”
It is not.
This Lambda is incapable of making an authorization decision.
Because the TAJ already made it.
The TAJ is manifest-pinned, compiled authority.
By the time a request reaches this Lambda, the only unanswered question in the system is:
“Given this exact manifest revision, where must this logical key go?”
Nothing about who.
Nothing about whether.
Only where.
III.1 What Most Lambda “Authorizers” Do
In typical systems, a Lambda authorizer:
- receives identity
- receives a request
- evaluates policy
- returns allow/deny
It is a PDP in disguise.
It is:
- stateful
- context-dependent
- security-critical
- difficult to cache
- hard to reason about under failure
That is not what RAJEE’s Lambda does.
III.2 What This Lambda Actually Does
RAJEE’s Lambda receives exactly two inputs:
- a logical key
- a manifest hash (from the TAJ)
And returns exactly one thing:
- the physical S3 destination for that key, if and only if that key is a member of that manifest
It performs:
- manifest retrieval
- set membership proof
- logical → physical translation
It does not know who the caller is.
It does not know what they are allowed to do.
It cannot allow or deny anything.
It can only prove where a request must go, given a TAJ that already proved the request is allowed to exist.
III.3 Why This Is Not “Just Routing”
This is not a lookup table.
A lookup table answers:
“Given this key, where should I send it?”
This Lambda answers a very different question:
“Given this manifest revision referenced by this TAJ, did this logical key exist in that manifest, and if so, where did it live at that exact moment in time?”
That is a historical, cryptographic, set-membership proof.
Not routing.
Not policy.
Not translation in the usual sense.
It is proving that there exists exactly one physical destination consistent with the authority already compiled into the TAJ.
This is precisely the operation that:
- IAM policies cannot perform
- bucket prefixes cannot express
- API Gateways cannot cache
- traditional routers cannot reason about
Which is exactly why this Lambda must exist — and exactly why it is safe for it to exist outside the security boundary.
III.4 Why This Has Excellent Operational Properties
These properties are not accidental.
They fall directly out of one fact:
This Lambda is not part of the security boundary.
Because authorization was fully decided when RAJA minted the TAJ, this component is allowed to be operationally boring.
If the Lambda:
- crashes → requests fail closed; no authority is widened
- is slow → caching is safe; results are immutable for a given manifest hash
- scales → trivial; the work is deterministic manifest queries
- logs → purely observational; it cannot influence outcomes
It is therefore:
- stateless
- deterministic
- aggressively cacheable
- horizontally scalable
- easy to replace (e.g., Quilt → DuckDB → in-memory index)
Most systems cannot say this about their “authorizer,” because their authorizer is secretly a PDP.
This Lambda is safe to treat like infrastructure, not security software.
And that is only possible because TAJ made the security decision earlier, at compile time.
III.5 The True Role: The Memberizer
This Lambda is best understood as a memberizer.
Given only:
- a manifest revision (from the TAJ)
- a logical key (from the request)
It proves exactly two facts:
- that this key was a member of that manifest
- where that member lived physically at that moment in time
That is the entire job.
It does not evaluate policy.
It does not interpret intent.
It does not make decisions.
It performs a deterministic set-membership proof over a historical manifest and returns the single destination that proof reveals.
Nothing more.
III.6 Why This Separation Matters
RAJA decides:
May this authority exist?
RAJEE enforces:
Is this request within that authority?
The Lambda answers only:
Where is it?
Security, enforcement, and routing are cleanly separated.
That separation is what makes the system understandable, debuggable, and maintainable.
III.7 The Insight
Most systems put policy and routing in the same place.
TAJ forces them apart.
And the Lambda memberizer is the proof that you can do large-scale logical aliasing without ever putting a PDP in the data path.
End of Appendix III
