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.
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".
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-indoes), 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'sallowAnonymousis 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,onturns it on, andoffturns it back off. It starts off, and a CLIpushleaves 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.
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.
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
- Who can see what: the access model — accounts vs handles, channels vs grants vs roles, and why your own alt handle is treated like a stranger.
- Access API reference — the full
access.jscontract. - Sharing & Access — the owner-facing sharing UI.
- Privacy and your followers — how a follower-labeled document actually reaches a follower.
- Local-first data and sync — what changing access does to data someone already received.