Field notes

Chapter 05 · Decisions

Part 2 · The foundations6 of 35

Permissions first in a shared family workspace

How we designed sharing for a household app: board membership as the unit, per-item shares, workspace-scoped queries, and a Vault the assistant cannot read.

The Margin4 min read

The Margin is meant to hold a household: shared boards for the things a family actually coordinates, split expenses, chores the kids can see, a calendar more than one adult writes to. That sentence is easy to say in week two and expensive to mean.

What "family workspace" costs the moment you say it#

The single-user version of this product has no hard questions in it. Everything in the database belongs to you, every query filters on one id, and the worst thing a bug can do is show you your own data twice.

Add a second person and every one of those assumptions breaks at once. Now there are things your partner should see, things your child should not, things you wrote in a shared space that you would like back, and an assistant sitting in the middle of it that has to know the difference.

You cannot add that later. It is a property of every read and every write in the system, and retrofitting it means revisiting all of them.

So we built it in phase two#

Before anyone was sharing anything. Before there were users to share with.

The order looked wrong at the time. We were writing a permission model for a product with one account on it, and a reasonable person could have called that premature. It is the decision I would defend hardest now.

Permissions belong to a small category of things that cannot be added to a system afterwards. They are in its foundations or they are missing from them. A product that adds sharing at a thousand users gets a migration and an audit, then a stretch where nobody is certain what is visible to whom.

The shape we landed on#

Boards are the unit. A board has members, and membership is what makes it visible, whichever workspace it lives in and whoever created it. That distinction earns its keep, because a person belongs to several workspaces at once and their boards travel with them instead of being trapped in whichever one they were made in.

Everything else derives from it. A card is visible if its board is. A comment is visible if its card is. Sharing is the data model itself, with no second system layered over it, so there is no separate path that could quietly disagree with the first one.

Later came a per-item layer on top: a single note or whiteboard shared with one person, without moving it into a shared space. Households do not organize themselves cleanly, and neither does anyone else. Someone always wants to show one person one thing.

Where a shared brain gets hard#

The harder problem is what the assistant is allowed to know.

If a family shares a workspace and the assistant reads everything in it, then the assistant becomes a way to learn things about the people you live with that they never told you. Nobody asked for that. Nobody would agree to it if you described it out loud.

The region scoped hardest#

So the memory the assistant holds is scoped the same way the data is, and one region is scoped harder than anything else. The Vault holds content the assistant cannot reach while it is locked. Nothing in it is retrieved, what the memory holds about it is encrypted under a key from your passphrase, and it must never turn up as an oblique mention in an answer about something else. It keeps things out of AI's reach and off the screen; it is not a lock against the people you share the workspace with.

That last part is where the real work went, and where we have already been caught out. Hiding something from a list is easy. Making sure it cannot leak through an inferred connection, a graph edge, a hover state, or an answer synthesized from adjacent facts is a much larger claim. The Vault leaked once, through the cards on a sealed board, which has a chapter of its own. The uncomfortable part is that the leak came through a background indexer nobody associated with the Vault at all.

Why we state it as an absolute#

Every query that touches shared content filters by the active workspace, and every one that touches board content joins through membership. Every one, including the ones added at eleven at night for a small feature nobody will notice.

That sounds pedantic until you watch it fail. A missing filter does not throw. It returns MORE rows, which looks like the feature working well, right up until the row belonged to somebody else.

There is no test that catches "this query returned data it should not have" unless you already knew which data that was. So the rule has no exceptions.