Skip to content

Documentation

Spaces, and letting somebody else read one

A Space is three things at once: a rule about what gets filed there, an audience it is read by, and a retention policy. It is not a folder, and almost everything surprising about Spaces follows from that. Two of the three are built; the filing rule is a design that is not connected to anything yet, and this page says so where it comes up.

A memory is not in a Space

Filing writes a row in a link table — space_memberships, one per memory-and-Space pair, carrying how it got there, how sure that was, and the reason in words. The column has three values — explicit, inferred, rule — and today every row is explicit, because nothing files anything automatically. See below. Three consequences, and all three are the point:

  • One memory sits in several Spaces

    A decision about the ledger can be filed in Work, in Acme and in Architecture. It is still one memory. Copying it per Space would make three that drift apart the first time any of them is corrected.

  • A correction reaches all of them

    Because there is one row to correct. Supersede it once and every Space it was filed into shows the new version and keeps the old one readable.

  • Removing it from a Space does not remove it from you

    The membership is ended, not deleted, so a re-add is visibly a re-add. The memory itself is untouched. Deleting a whole Space is the one place you have to say which you meant — memories: keep or delete, with no default, because guessing loses somebody’s material either way. Even then a memory is only destroyed if this was the last Space holding it.

Universal is the floor, not a Space

no membership row

space_memberships

Every memory on your account is in Universal by definition, so there is deliberately no row saying so — it would be one row per memory carrying no information. Asking “what does Universal hold” is asking what your account holds. Asking for Universal plus two Spaces is that same question, unioned with the membership rows for those two.

This is why a Space cannot lose you anything. The Space is a view; the memory lives underneath it.

Where something gets filed when you did not say

Read top to bottom; the first one that has an answer wins.

A caller that names Spaces

spaceIds on remember, on an upload, on a connection, or on a conversation. An explicit choice is never second-guessed.

every surface

A folder that was set up

pm setup writes a Space id into .persistmemory.json beside your code, and every command in that folder uses it. --space overrides one command; PERSISTMEMORY_SPACE overrides a shell.

CLI

A Space an assistant was told to work in

use_space fixes where captures are filed AND where questions are answered from, until it is changed. current_space reports which — so a narrow answer is not read as a complete one.

MCP

The conversation it came from

A conversation carries its own space_ids, and extraction inherits them. Turns appended to a conversation filed in Acme become memories filed in Acme.

chat, Telegram, Meet

The account default

When nothing above said anything. One policy in one file, so the CLI and a chat cannot disagree about where an unattributed note landed.

fallback
A Space does not file anything by itself. Every row above is somebody saying where something goes — a caller naming a Space, a folder that was set up, an assistant told to work in one, or the account default. There is no rule that reads a memory and decides. The pieces for one exist — Spaces can hold a set of rules, and there is a classifier that turns entity names and types into proposals — and they are deliberately not connected: there is no way to write a rule, no way to review a proposal before it takes effect, and filing into a Space you have shared is a disclosure rather than a tidying, so a wrong guess would be read by somebody else. Until those are answered, nothing is inferred and nothing is filed on your behalf beyond the default in the last row.

The audience rule

A Space has an audience — how wide its own readership is. A memory has a scope, carried from the source it came from and never widened. Filing compares the two, and refuses when the Space is wider than the memory. A Space no wider than the memory adds no readers, so it is allowed; a Space wider than it would add some, so it is not.

Said plainly: organising your notes cannot quietly become a disclosure. You can put a public fact into your most private Space. You cannot put a private one into a Space other people read.

scopewho that means
privateOne person. What a provider did not label also lands here — silence is read as the most restrictive answer, not the most convenient one.
sharedThe people a source named. A DM, a file shared with three colleagues.
spaceA Space's own readership. Defined in the ladder and not yet produced by anything: no connector labels material this way today, so you will not see it on a memory.
workspaceAn organisation. Drive files marked organisation-wide arrive at this width.
universalPublic. A published page, a post anyone can read.

Narrowest at the top. A memory drawn from several sources keeps the narrowest of them, because something derived from a private source is as protected as that source.

What the refusal looks like. Adding memories to a Space answers 409 with the sentence “That memory is more private than <Space>, so filing it there would show it to people it was not meant for. Nothing was changed.” The whole request is refused rather than partly applied — filing four of five and reporting success would leave you believing all five landed.

A Space you make by hand has no declared audience, and an undeclared audience is read as private — so nothing is refused until a Space is genuinely read by more people than its owner.

Sharing a Space

A Space can be given to another person to read. The record is a grant — space_collaborators — and it is the first thing in this system that lets one person see another person’s memories, so what it does not do matters as much as what it does.

It grants sight, never ownership

A collaborator reads the owner’s memories through the Space. Nothing is copied into their account. That single decision is what makes revocation one update rather than a cascade nobody can reason about: end the grant and the memories are gone from their next query, with no orphaned copy and no citation pointing at something they can no longer open.

Read only, and no roles

Every grant means exactly one thing: this person may read this Space. There is no writer and no admin, because nothing would honour one. Every write still matches on the owner’s id, so a shared Space never hands out edit rights on somebody else’s memories.

Being invited grants nothing

Access begins when the recipient accepts, and retrieval checks for the acceptance rather than the invitation. The case this was built for is matching meeting attendees by address — and an attendee list is chosen by whoever booked the meeting. If an invitation granted access, a stranger could put their own material inside your assistant by scheduling a call with you.

Their contradictions stay theirs

Conflicts are scoped to one person’s record and settled by supersession. Because nothing is copied, a memory you can see through a shared Space never enters your conflict set — it cannot supersede something you believe. Your assistant can still cite it: “the meeting note says X, though you recorded Y”, which is the honest answer anyway.

Where this has got to. The grant, the acceptance rule and the read path are built: a memory filed in a Space shared with you and accepted by you comes back in your searches, and revoking removes it from the next query. There is no endpoint and no screen for sending, accepting or revoking an invitation yet, so today a grant can only be made at the data layer. Nothing on this page describes a button that exists — it describes the rules that will govern one.

Retention

A Space may carry retentionDays: an age after which a memory filed here is worth reviewing, whatever its freshness. It exists because freshness decays from the last time something was mentioned, so a claim repeated every week stays maximally fresh however much the world has moved on.

Stored, and not yet swept. The value is saved and returned, and the policy that reads it is written and tested — but nothing runs it on a schedule, so setting it today changes nothing you will observe. Set it if you want the intention recorded; do not set it expecting anything to be archived.

Doing it

Over HTTP

GET    /api/v1/spaces                 your Spaces, paged
POST   /api/v1/spaces                 { name, description?, kind?, parentId?, retentionDays? }
GET    /api/v1/spaces/default         the account fallback
PATCH  /api/v1/spaces/default         set or clear it
POST   /api/v1/spaces/merge           { sourceIds[2..20], name } — sources are left alone
GET    /api/v1/spaces/{id}
PATCH  /api/v1/spaces/{id}            { name?, description?, retentionDays?, archived? }
DELETE /api/v1/spaces/{id}            { memories: "keep" | "delete" }  — required
GET    /api/v1/spaces/{id}/memories   what is filed here
POST   /api/v1/spaces/{id}/memories   { memoryIds } — refused if it would widen the audience
DELETE /api/v1/spaces/{id}/memories   { memoryIds } — unfiles, never deletes

Membership is a sub-resource with its own POST and DELETE rather than a field on the Space, so adding one memory does not mean reading every id and sending them all back — which is how two clients silently lose each other’s changes.

From a terminal

pm spaces list
pm spaces create "Acme"
pm spaces delete "Acme" --memories keep     # or --memories delete
pm spaces merge "Work" "Personal" --name "All"
pm setup --space "Acme"                     # this folder, from now on
pm search "why postgres" --space Acme,Work  # one command, by name or id

From an assistant

list_spaces turns a name into an id, use_space fixes the one this connection works in, and current_space says which. The choice steers filing and retrieval together — a default that filed one way and searched another would put material somewhere the next question could not see. See the MCP server.

From a chat

/spaces lists them and /space <name> sends everything from that chat to one, until /space none.

A Space is not a permission boundary between you and us, and it is not encryption. It decides which memories answer which question, who else can read them, and how long they stay interesting. Everything in every Space belongs to one account until a grant says otherwise.