Vibes DIY
Vibes DIY / Docs
creator docs · concepts · access

Two layers of access: the envelope and your app's rules

Every question about who can see what on Vibes DIY has two answers, and they come from different places.

The first is who may be near this app at all — is it open to the world, open to look at but not to write in, or only for a list you approve. That is the envelope: it belongs to whoever deployed this copy, it lives in app settings, it is set in one word, and it changes without touching the source.

The second is what each document means — this one is public, that one is for the author's followers, this other one is for a topic only a few people were let into. That is your app's access function (access.js). It ships with the code, and everyone who copies the app gets it verbatim.

The layers nest. The envelope is outside; your rules run inside it.

THE ENVELOPE Who may be here at all Set in app settings by whoever deployed this copy — not in the code. YOUR ACCESS FUNCTION What each document means access.js — ships with the app. Everyone who copies it gets these rules. Public Anything routed to a public channel. Followers Labeled for the author's followers. Topic · private A channel a few people were let into. One database, pinned tighter A rare refinement of the same envelope. Inner layers only narrow Your access function can restrict further, but it can never hand out access the envelope already refused. And the envelope never decides what your documents mean — that stays yours. Two questions, two owners: the person running this copy answers the first; the person who wrote the app answers the second.
The envelope belongs to the deployment; the access function belongs to the app. A read or a write has to satisfy both.

What each layer is for

The split is not arbitrary — each layer answers a question only its owner can answer.

The envelope Your access function
Question Who may be near this app? What is each document, and who is it for?
Owner Whoever deployed this copy Whoever wrote the app
Lives in App settings access.js, in the source
Travels with a copy? No — a copy starts closed Yes, verbatim
Can it widen the other? It sets the ceiling Never — it can only narrow

The practical consequence is the interesting part: the same app can run as a public playground and as a locked team tool, with identical code. Nothing about "make this private for my team" belongs in your app's logic.

"Inner layers only narrow" is exact, not a rule of thumb. A per-database pin is intersected with the posture, so it can only subtract; nothing inside the envelope can widen it, and the one place the envelope reaches further than its posture — taking writes from signed-out visitors — is a setting of its own on the outside, never something an app can grant itself.

An app with no access function is flat

If an app has no access.js at all, there is no second layer — the envelope's answer is the whole answer. On an app published openly, that means every document in it is readable by anyone. Not "public documents are public": everything, because nothing is there to sort one document from another.

One clarification about "signed-in people may write" throughout this page. An envelope is set in one word — its posture — and the open envelope drawn here is the posture called open, whose writing rule is the group signed-in: anyone with a Vibes account, whether or not you have ever heard of them. Nobody is being made a member or approved behind the scenes; the posture simply reaches that far. An owner who wants less picks a different posture — gated keeps the app readable by anyone while holding writing back for people they approve, and private is the team copy later on this page.

The three postures, and what each one means:

Posture Who can read Who can write In plain words
open anyone signed-in Reachable by anyone; documents governed by your app's rules.
gated anyone members Anyone can open it; the people you approve can write.
private members members Only approved members can open this vibe.

In the app, the switch under the Share card's Share settings reads Restricted or Public. Restricted is private: "Only the people below can open this app." Public is the published default, open for an ordinary app and gated for one that declared itself privacy-sensitive: "Anyone with the link can open this app."

Note what open claims and what it does not. "Reachable by anyone" is a statement about the room, not about your documents — an open app whose access function routes most of its documents to channels strangers are not in is still an honest open app. It never means "all your data is public".

ENVELOPE · OPEN Published to the world NO ACCESS FUNCTION Nothing sorts one document from another, so every document is readable. Signed out reads everything Signed in reads everything, and can write Signed out, writing saved on their device only This is why taking your app public asks for an access function to be written first. It is the layer that turns "everything is readable" into "this is public, that is for my followers."
The floor, drawn so you can recognize it — not a setting to aim for. A new vibe starts private, and flipping it public waits until the app has an access.js — so an app sits here only when its owner explicitly chose to go public without access controls.

Two things worth pinning down about this floor:

  • It is the reason going public waits for an access function. A new vibe starts private — visitors request access — and can stay that way without an access.js. Flipping it public is the moment this floor would become real, so the flip holds until the app has one: asking to go public on an app with no rules asks the builder for them, and the app goes public when the version being published actually contains them. Reads only follow a grants model once something labels the documents; until then the envelope's "open" means open to everything inside. (You can decline and go public without access controls — that choice is exactly this floor, on purpose.)
  • Public writing is never just a setting, and never just code either. No posture on its own lets the signed-out world write into your app's shared data. A signed-out visitor's writes are saved on their own device; they join the shared data when that person signs in and the posture admits them (on open, signed-in does), or when writes from signed-out visitors clear both halves of the envelope: your access function has to accept and validate the write, and your deployment's own anonymous setting has to admit anonymous writers. The function's allowAnonymous is a proposal; the setting is what ratifies it, and it belongs to whoever runs this copy — it is recorded when the app is published, and it is theirs to withdraw, by publishing again or changing the deployment's settings. The Share card does not surface it yet. From the command line you can read it and change it: vibes-diy app anon-write <handle>/<app> shows it, on turns it on, and off turns it back off. It starts off, and a CLI push leaves it as it was. Neither half is enough alone.

The same app, in two different envelopes

Here is what the split buys. On the left, an app published for everyone. On the right, the same app — same source, same access.js — copied and run for a team, with the envelope tightened to an approved list.

Published for everyone ENVELOPE · OPEN Anyone may look; signed-in people may write. ACCESS.JS Public what a signed-out visitor sees Followers resolved against the follow graph Topic · private only people let into the channel Signed out → reads the public documents Signed in → can write Copied and run for a team ENVELOPE · APPROVED LIST Only people the copy's owner approved. ACCESS.JS — UNCHANGED Public now means: everyone on the team Followers followers who are also on the team Topic · private unchanged — still just those people Signed out → a request-access screen Approved member → can write SAME CODE
Tightening the envelope narrows every group the app routes, automatically. The app is never told it was contained — which is why running it for a team is a settings change and not a fork.

Notice what happened to the app's own categories on the right. "Public" did not stop meaning public — it still means everyone who may be here, and the envelope changed who that is. A follower who is not on the team simply is not here to read anything. Your rules did not change; the room they run in got smaller.

There are two ways somebody becomes one of the people your envelope means. They can open the app, ask for access, and you say yes — that is the request waiting under Members in your Share settings (tap the Vibes switch, then Share, then Share settings). Or you can add them yourself: under Members, invite them by handle or email. A handle invite lets them in straight away, with nothing to accept, and they get a notification saying so; an email invite waits until they accept it. Everyone you invite arrives with Data: Read. Change Data to Write on their row to let them write too, or set Code to Yes to let them change the app's code. Use this when you already know who you are building for — a teammate, a friend you want editing alongside you — instead of waiting to be asked. You can invite your own other handle the same way, and it is the easiest way to see your app as two people: to everybody else your two handles are two different people, so the second one arrives as an ordinary member and your rules treat it as one.

One more thing sits in Share settings, just under the switch that decides who can open your app: Only editors can change data. Turn it on and everyone who is not an editor reads your app without being able to add to it or change it — readers, people who only submit, and (if you had them on) anonymous visitors. Editors and you keep writing to the app's databases.

It is worth knowing why that switch exists, because the default surprises people. A member with Data: Read can still write to any database your app has not specifically locked down: "Read" describes what the app shows them, and membership is what the envelope checks. This one switch closes that for the app's databases at once, so reader means reader across them. It is a write-side decision only — who can open your app is still the Restricted/Public choice above it — and it applies the moment you flip it, to every visitor and every app database. Two things it does not move: a narrower per-database pin or access-function rule still constrains editors too — inner layers only narrow — and the platform's media: namespace sits outside this envelope entirely.

Those two ways are the whole list, and it is worth being precise about it, because the layers make it easy to expect otherwise. Your access function can name a person on a document — letting a friend into one trip, one board, one thread. That decides what that person is shown once they are inside the envelope; it does not get them through the envelope. So on a copy running for an approved list, naming a friend in your rules does nothing for them until they are also on the list — the request they send, or the invite you type. The rule of thumb is the nesting itself: the inner layer can only narrow, never widen, and letting somebody new in is the widest thing there is.

One thing tightening an envelope does not do: reach backwards. Moving a copy from open to private, or removing someone from the list, stops them receiving your data from that point on — it does not erase what already synced to their device. Local-first data and sync has the full picture.

This is also why hand-rolling "only my team" into access.js is the wrong move. It hard-codes one deployment's membership into logic that every copy inherits, and it leaves you maintaining a fork.

Where the follower rules live

The Followers group above is the one place a third party has a say. Your app labels a document for its author's followers — but whether a given follower actually receives it is a conjunction of three separate decisions, held by different people.

A follower wants to read Ana's document DECISION 1 The follow edge Ana and her follower, on the platform graph. Public account: a follow takes effect at once. Private account: a request Ana approves first. AND DECISION 2 Ana's choice for this app Held per app, by Ana — not by you, not by the app. Ordinary app: shared with followers already. Privacy-sensitive app: off until she says yes. AND DECISION 3 Your label The one part your app controls. You labeled this document for Ana's followers, and the platform resolves it against the live graph as it is read. read All three sit inside the envelope: on a team copy, the follower has to be on the team as well. Any one of them saying no is enough — and only the third is yours.
Three decisions, three holders: Ana and her follower form the edge, Ana alone chooses per app, your app labels the document. Your label is a request to the platform, not a grant.

The full order those checks run in — including the handle-wide setting that sits between them — is on Privacy and your followers; follows, private accounts, and blocking are on The Social Graph.

Two things from those pages are worth carrying into this one:

  • Follows are graph-level, never per-app. No app setting can block, hide, or downgrade a follow. An app taps the platform's one follow graph instead of building its own list.
  • Marking an app privacy-sensitive changes the defaults, not the ability. An ordinary app shares follower-labeled documents with followers with no separate consent step; a sensitive app never turns that on by itself.

At a glance

Who can do what, in each of the three situations on this page:

Open, no access function Published for everyone Copied for a team
Signed out, reading Everything The public documents A request-access screen
Signed out, writing Saved on their device Saved on their device, unless the app accepts anonymous writes and its owner-visible anonymous setting admits them Not here at all
Signed-in visitor, writing Yes Yes, if your rules accept the write Only once approved
Follower, reading follower-labeled docs No such thing yet Yes, per the author's privacy choices Yes, if they are on the team too

See also