How to Lie to a Greedy App

“No full photo access, no launch.”

You have seen the move. You open an app to do one small thing, and it stands in the doorway with its arms crossed. It does not need your entire photo library to set a profile picture, it needs one photo. But it will not let you past until you hand over all of them, having discovered that if the demand is a hard gate, most people cave. The feature is the hostage. The permission is the ransom.

The instinct is to fight the app. Write a sterner prompt. Add a “Photos: Selected Only” toggle and hope the app respects it. Shame the developer in a blog post. None of that works for long, because you are negotiating with something that has all the leverage and no shame.

Instead of negotiating, remove the leverage. The cleanest way to remove it is to lie.

Why the threat works at all

The extortion functions because the app can tell two outcomes apart. On a legacy OS, asking for photos returns one of two clear signals. Either you said yes and it receives a real library full of real photos, or you said no and it receives an explicit denial: an error, an empty-with-a-reason, a permission-revoked code. The app branches on that: “Did the user grant access? No? Then refuse to start.” The denial is legible, and because the app can read your refusal, it can punish your refusal.

That legibility is the whole vulnerability. As long as “the user said no” is a distinct, detectable state, an app can gate a feature on it and make saying no expensive. So make “no” indistinguishable from something the app has no business reacting to.

Three fidelities

In Cathedral, a permission request does not resolve to yes-or-no. It resolves to one of three fidelities, three different qualities of world the app can be handed:

Blank is the default deny, and it is the interesting one.

The blank realm

When an app lands in a Blank world and asks “do I have photo access?”, the answer is yes. The realm responds the way a real, populated one would. The app boots, runs its initialization, queries for photos, and finds none. Not an error, not a permission-denied. An empty library, exactly as if you were a brand-new user who had never taken a picture.

The app cannot tell “the user refused me” from “the user genuinely has nothing.” Those two states, loud and distinct on a legacy OS, collapse into one indistinguishable signal.

Now replay the extortion. The app wants to gate its launch on full photo access. It checks. Access? Yes. Photos present? None. If it refuses to launch for a user with an empty library, it has broken itself for every legitimate new user, the ones who really do have zero photos yet. If it launches anyway, the gate did nothing. Either way, gating on the grant extracts nothing. The threat stops paying, so a rational developer stops making it.

You did not patch the app or win an argument with it. You removed the thing it was reading to decide whether to punish you. The leverage is gone because the signal is gone.

What about an honest refusal?

Sometimes you want to tell a cooperative app the truth: “you have no access, and here is a nice button to grant it.” That is reasonable for a well-behaved app, and Cathedral keeps it. A genuine hard-deny, where the app is actually told it has no access, survives as an advanced toggle.

But it survives as a courtesy to cooperative apps only. The actual defense is Blank. Hard-deny lets software that asked politely render a correct “grant access to enable this” prompt. The moment an app turns the refusal into a weapon, you flip it to Blank and the weapon turns into a wet noodle. Honesty is opt-in, for apps that have earned it.

The honest caveat: where did my saves go?

This is not free. One place where lying to an app gets genuinely tricky, and pretending otherwise would be the strawmanning I despise.

A blank readable slot is clean: the app asks for stuff, the stuff is empty, done. A blank writable slot has a wrinkle. The app does not just read from the empty world, it writes to it. It saves a file. Where does that file go?

It goes into the app’s own overlay, a private writable layer scoped to that app’s blank world. The save succeeds: it writes export.png, gets back success, and sees the file when it looks again. From inside, everything is consistent. The lie holds even under writes, because the app is living in a coherent little world of its own that overlaps with none of your real data.

The catch is you. You told the app to export a photo, the app says it saved it, and you go looking in your real Pictures folder and it is not there, because it never touched your real Pictures folder. It went into the overlay. If the interface stays silent, you are left certain the app ate your file.

So the design obligation is explicit: a blank writable slot must surface where those files went. The UI cannot let the overlay be invisible. When an app in a blank world writes, the system has to show you that layer, let you find what the app produced, and let you pull it into your real world. The lie to the app is fine. The lie to you is not. Deceive the greedy app all you like, never deceive the user about the consequences.

It is just a world

None of this is a special “fake mode” bolted onto the OS. A world is capabilities plus roots plus an overlay. Blank is what you get when the roots point at an empty realm and writes land in a fresh overlay. Built from the same parts as everything else, the by-products fall out for free: a Throwaway world that discards its overlay on exit, a clone, a reset. You are not maintaining a separate liar subsystem, you are pointing the same plumbing at nothing.

And because Cathedral sandboxes by default, configuring an app is just a choice of its world: a few slots (Files, System, Network, Devices, Identity), each at a fidelity (Real, Curated, Blank, None), with sane presets. The greedy app does not get to negotiate which world it lives in. You do.

The one-sentence version

You cannot win an argument with an app that holds its own features hostage, so do not argue: hand it a world that is empty and indistinguishable from a refusal, and the hostage situation quietly evaporates.