Skip to content

Share a memory with someone

CLIAPIIntermediate~10 min

A memory share is a per-user grant: you hand one named person reader or writer access to your own memory, without adding them to a team and without changing who owns it.

It's the narrowest of Hadron's access mechanisms. If you want a group with symmetric access, you want team membership on a group-class memory instead; if you want a whole organization to read something, you want a knowledge memory and org membership.

What can be shared

Only personal-class memories. That's the whole rule, and it's worth internalising because the failure mode is opaque (see Troubleshooting).

Class Shareable? Use instead
personal Yes
private No — owner-only, by design Nothing; privacy is the point.
knowledge No Org membership, or a cross-org subscription.
group No hadron memory member — symmetric team access.
app, system No App/Agent scoping.

Who you're allowed to share with

There's a second rule on top of the class, and it depends on whether the memory belongs to an org:

  • An org-owned personal memory can be shared only with a current member of that org. Naming an outsider fails with CrossOrgShareNotAllowedError.
  • A free-standing (org-less) personal memory — one created with --owner-me — can be shared with anyone.
  • Agent-scoped personal memories are exempt from the org-membership restriction above — their audience is governed by App membership and the user's Agent subscription rather than by org membership. Shares still apply; in fact this is the pattern shares were built for (see below).

Shares are how per-pairing isolation works

On an Agent-scoped personal memory, the share is the mechanism: Alice's memory paired with Mentor A is a different memory from the one paired with Mentor B, each with its own share. So don't read the exemption above as "shares don't apply here" — it only means the grantee doesn't have to be an org member; App membership and the Agent subscription govern instead.

Before you start

  • You must be the memory's principal — its owner. Only the principal can grant, change, or revoke a share on it. There's no admin override.
  • You need a way to name the grantee. Any of these work:
usr_01hx…                 the user's id
jane@acme.com             email
@jane                     handle
hrn:user:jane             the user URN

The server resolves the reference against the full user table, so sharing a free-standing memory by email or handle works even when you have nothing else about the person.

1. Grant access

hadron memory share create acme.com:my-notes --grantee jane@acme.com --role reader

Two roles, and the difference is exactly what it sounds like:

Role Can
reader Read every node in the memory.
writer Read, and create, edit, and delete nodes.

Neither confers ownership. A share never lets the grantee re-share the memory, delete it, or manage its other shares — those stay with the principal.

create upserts: naming someone who already has a share updates their role instead of failing, so it's safe to re-run.

2. See who has access

hadron memory share list acme.com:my-notes   # `ls` is an alias

Each row shows the grantee, the role, and when it was granted. The grantor is always the principal — even when an App backend made the call on their behalf, in which case the acting party is recorded separately in createdBy.

3. Change a role

hadron memory share set-role acme.com:my-notes --grantee @jane --role writer

Re-running create with a different role does the same thing; set-role just says what you mean.

4. Revoke, or leave

The principal removes someone else's access by naming them:

hadron memory share rm acme.com:my-notes --grantee @jane --yes

A grantee removes their own access by omitting --grantee — the "stop sharing this with me" path:

hadron memory share rm acme.com:my-notes --yes

Two things to know about revocation:

  • It takes effect on the next read. There's no deactivated state — the row is deleted — so an in-flight request may still complete.
  • Revoking as the principal is idempotent: removing a share that's already gone succeeds. Leaving is not — the share row is your authorization, so a second leave answers FORBIDDEN (you already left).

A grantee can leave a memory even after its owner has soft-deleted it, so a share can't strand you.

Troubleshooting: FORBIDDEN is deliberately vague

If you're not the principal, every failure looks the same. FORBIDDEN does not distinguish between:

  • the memory doesn't exist,
  • the memory isn't personal-class, or
  • you aren't its principal.

That's intentional — collapsing them stops the API being used to probe which memories exist. The same uniformity covers the grantee reference: a non-principal gets FORBIDDEN whether or not the named user exists, so users can't be enumerated either.

So if a share fails and you are the owner, check the class first — a private or knowledge memory will refuse a share and tell you nothing more. As the principal you will get a distinct NOT_FOUND when the grantee reference resolves to nobody, which is the one case that's safe to report.

Other named failures: CrossOrgShareNotAllowedError (the grantee isn't a member of the org that owns the memory) and MemoryShareGranteeMissingError (the reference didn't resolve to a user).

Other surfaces

  • GraphQLcreateMemoryShare / deleteMemoryShare / the share list, with granteeRef taking the same id / email / handle / URN forms. (The older granteeId argument is deprecated in favour of granteeRef.) See the GraphQL API reference.
  • Portal — you can see memories shared with you, and leave one, from the memories list. Granting a share is not in the portal yet, so use the CLI or GraphQL for that.
  • MCP — no share tools yet.