Cavu Games is a collection of silly browser based games that I've been building as part of a bigger project, but while each game is reasonably simple as a whole it's part of an exploration into something much more technically complex that I'm going to spend 3 blog posts discussing. This is Part 1 covering the Intent to Launch, Part 2 will cover engine design, Part 3 will cover local development and future work on the games forge.

Let's Play

Some of the games over on Cavu Games are open for play by guests but all of them play best when you're signed in. They get to do things like write a atmosphere.games now playing record that lets other people know you're playing a game, they get to use your rpg.actor sprite as a player sprite, games like Fright Fest leverage your follows list to build your party. This is the power of the atmosphere, but this power isn't free.

OAuth

OAuth is consistently one of the things people seem to be most scared of in building to the Atmosphere. It was highlighted in the Where We're At 2026 survey conducted in October as complex and confusing.

Since I've been building lots of little ATProto applications I've had the pleasure of implementing OAuth many times. At this point I've got a playbook for implementing it and it's not scary anymore. Still I do have a beef with OAuth.

Currently if I have 3 games hosted at 3 different domains I would need 3 different oauth sessions.

If I'm not scared of OAuth sessions what's the big deal having 3? The short answer is User Experience, Every OAuth Session is also a Login, and a Consent Prompt. Repeated login prompts like this can have a few major drawbacks.

  1. 1.

    Friction - If you played one game I made then you may want to play another one, but to do that you need to log in again, that means another chance to decide that it's not worth it and bounce.

  2. 2.

    Lack of Trust - If each site asks you to trust it, each one begs the question "should I?"

  3. 3.

    Diffusion of Trust - If every action seems to take you to a consent prompt you're going to stop reading the prompt.

Let's Solve Auth for Everyone

On Cavu Games you log in once and get to play lots of games (7 at time of writing). None of those games require you to log in again. I could achieve this by having a single shared backend but the goal was to be able to solve this problem for everyone. Anyone could build a game (or something else) and host it on their own site and skip the OAuth prompt. How I do that is the Intent to Launch flow.

Intent to Lauch is made up of 3 actors on the backend.

  • Cavu Games: The listing site that you're going to log into

  • Your PDS: Where the writes are actually going

  • The game server - in this case play.cavu.games: Where the writes are going to originate from

Login

Intent to Launch really starts with Logging into cavu.games, that's pretty standard OAuth flow, we use permission scopes and request permissions in order to write to the records that games need to write to. The goal is to have one trusted place where people log in, one thing to authorize. Additionally if you're on the cavu.social pds then you don't even see the consent screen. A pattern that we may want to explore more.

Once you've logged in you can browse and select a game.

Declaring Games

On Cavu Games each game lists what permissions it needs in plain text, these come from a manifest that is on the game declaration. Currently games are limited to using public atproto records so these include things like writing save records, and updating stats on rpg.actor.

While it's important for us to disclose the writes that would be taken on your behalf Cavu Games is structured to require you to place no very little trust in these actors.

Once you've decided what game to play click the play button and watch as lots of stuff happens behind the scenes that seems very simple on the frontend.

Session Grant

When you click play on cavu.games we start a launch session on the backend. That begins with connecting to a declared launch endpoint, each game has one. Cavu Games sends along a nonce and gets back a signed grant. The grant contains a launch URL, Cavu Games then triggers a redirect to the launch URL on the users browser. The Grant is appended to the url as a query parameter.

As the browser starts a game via the declared launch url the grant is grabbed from the URL and passed on the backend to Cavu Games. This triggers session binding: A long lived connection from the game server to Cavu Games that is authorized to write on behalf of the user. With the session bound the actual game starts.

Play

When you're playing a game your interaction is with the game server via whatever it has served to your client. Whenever the game would like to write records to your PDS, such as storing a game save, updating stats, or writing custom codex records it does so by using the bound session with Cavu Games. The Cavu Games server is then responsible for the actual writes to the PDS.

From a trust standpoint Cavu Games is not just a Proxy to a PDS. It's able to enforce a the permissions that the game declared in their manifest, and more importantly it's able to put restrictions on the record keys that a game can write.

Every game on Cavu Games has a unique slug that identifies it. This slug has to either be the entire record key, or a required prefix on the record. That's important because it prevents a case where a malicious game could erase progress on another game, or attribute progress that didn't actually happen.

Next: Step Up

One thing we don't currently support with Cavu Games but will likely be coming next is Step Up authentication. This is how a Game can break out of it's slug prefixed standbox. For instance if you want to write a bluesky post from the game.

The approach we're taking with this to force the delegation through Cavu Games via iframe. The game itself would send an intent to Cavu Games, such as: Bluesky post. Cavu Games would then return a unique url that renders a confirmation dialog. The game would open it in an iframe. Cavu Games would check that the person accessing the url is the same one it was generated for and then if the action was confirmed take action. This continues the trend of "We don't need to trust the game" since the site we do trust is responsible for verifying user intent.

Where do we go from here

is doing really cool work with webtiles, and is looking at how webtiles can read and write data on a PDS.

While what I've built with Cavu Games is specifically games related, and currently is only connecting to a game server I own, the approach is general for all Auth.

There are a few big apps in the Atmosphere like BlueSky, Blacksky, Northsky, Witchsky, and mu social that basically require full transition:generic level permissions. Having these apps act as a gateway to other smaller apps that are granted a limited set of permissions could be a way to smooth over the transition between atmosphere apps and help with the feeling of a collective whole.

The approach I've built in cavu games works and it's generalizable however it requires all the data to pass through a server I operate on the way to your PDS. A better approach would be a way to ask the PDS to mint a subsession. A DPOP bound session that would be scoped at or below it's parents scope, and revoke with it's parent session but which could live independently. This would let each game write to the PDS directly and make the approach much more viable.

We've spent a lot of effort thinking about how we communicate "Log in with the Atmosphere" to people, but what if we didn't need to. What if The Atmosphere was lots of little composable experiences we could have. That could mean a rich set of choices in where you start in the Atmosphere, how you move through it, and what experiences you have along the way.

MORE TECHNICAL STUFF

Check out the cavu games repository for how this is all actually built. https://tangled.org/pardy.at/cavu-games