Share a Space
Give a teammate, a service identity, or every authenticated principal access to a memory space.
Share a Space
A new space is usable only by its owner; everyone else's requests against it are denied. To change that, you either assign a space role or create individual grants. This guide covers both, plus opening a space to every authenticated principal on the instance.
Sharing works the same way for human users and service identities. Both are principals, and both are identified by UUID.
Before You Start
- Authority to administer the space's access. The space owner has this automatically; so do a
SPACE_ADMINon the space, an instanceADMIN, and the instance owner. - A credential with that authority: the
goodmemCLI authenticated, or an API key for REST.
export GOODMEM_REST_URL="https://localhost:8080" # REST base URL; on GoodMem Cloud, your instance's https:// hostname
export GOODMEM_API_KEY="gm_your_key"The CLI speaks gRPC, which GoodMem Cloud instances do not expose. On Cloud, use the cURL or HTTPie tab — every step here is a plain REST call over HTTPS — or the space's Access tab in the console. If your own server uses a self-signed certificate, add -k to curl and --verify=no to HTTPie.
- The UUIDs involved. Look up the space and the teammate:
goodmem space list
goodmem user get --email [email protected] --format jsoncurl -sS "$GOODMEM_REST_URL/v1/spaces" --header "x-api-key: $GOODMEM_API_KEY"
curl -sS "$GOODMEM_REST_URL/v1/users/email/[email protected]" --header "x-api-key: $GOODMEM_API_KEY"http GET "$GOODMEM_REST_URL/v1/spaces" x-api-key:"$GOODMEM_API_KEY"
http GET "$GOODMEM_REST_URL/v1/users/email/[email protected]" x-api-key:"$GOODMEM_API_KEY"Export them to keep the commands short:
export SPACE_ID="70e025f6-76ca-4cbe-b8fc-7dab8e84590a"
export PRINCIPAL_ID="b3303d0a-1a4a-493f-b9bf-38e37153b5a2"1. Assign a Space Role
Roles are the usual way to share. Each one bundles a sensible set of capabilities on a single space:
| Role | What it adds |
|---|---|
SPACE_VIEWER | Read the space, list its memories, read its memories |
SPACE_CONTRIBUTOR | Viewer, plus create memories |
SPACE_CONTENT_MANAGER | Contributor, plus delete memories |
SPACE_ADMIN | Content manager, plus update the space and manage its access |
Exact capability lists are in the built-in roles reference.
goodmem space access role assign $SPACE_ID \
--principal $PRINCIPAL_ID \
--role SPACE_VIEWERcurl -sS --json @- "$GOODMEM_REST_URL/v1/access-policy/role-assignments" \
--header "x-api-key: $GOODMEM_API_KEY" <<JSON
{
"principalId": "$PRINCIPAL_ID",
"role": "SPACE_VIEWER",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" }
}
JSONhttp POST "$GOODMEM_REST_URL/v1/access-policy/role-assignments" x-api-key:"$GOODMEM_API_KEY" \
principalId="$PRINCIPAL_ID" role="SPACE_VIEWER" \
assignedResource:="{\"kind\": \"SPACE\", \"resourceId\": \"$SPACE_ID\"}"A role assignment applies to the one space it names. SPACE_VIEWER on space X says nothing about
space Y; share each space separately.
SPACE_ADMIN can manage the space's content and access policy, including assigning roles to
others. It cannot delete the space or transfer its ownership. Those stay with the space owner and
instance administrators.
2. Grant a Single Permission
When a role gives away more than you intend, create a direct grant instead. A grant pairs one operation with one selector on the space:
# Let one principal read the space record itself
goodmem space access grant create $SPACE_ID \
--principal $PRINCIPAL_ID \
--operation READ_SPACE --selector EXACT
# Let the same principal read every memory the space contains, now and later
goodmem space access grant create $SPACE_ID \
--principal $PRINCIPAL_ID \
--operation READ_MEMORY --selector DIRECT_MEMBERS_OF# Let one principal read the space record itself
curl -sS --json @- "$GOODMEM_REST_URL/v1/access-policy/grants" \
--header "x-api-key: $GOODMEM_API_KEY" <<JSON
{
"audience": { "principalId": "$PRINCIPAL_ID" },
"rule": { "operation": "READ_SPACE", "selector": "EXACT",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON
# Let the same principal read every memory the space contains, now and later
curl -sS --json @- "$GOODMEM_REST_URL/v1/access-policy/grants" \
--header "x-api-key: $GOODMEM_API_KEY" <<JSON
{
"audience": { "principalId": "$PRINCIPAL_ID" },
"rule": { "operation": "READ_MEMORY", "selector": "DIRECT_MEMBERS_OF",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON# Let one principal read the space record itself
http POST "$GOODMEM_REST_URL/v1/access-policy/grants" x-api-key:"$GOODMEM_API_KEY" <<JSON
{
"audience": { "principalId": "$PRINCIPAL_ID" },
"rule": { "operation": "READ_SPACE", "selector": "EXACT",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON
# Let the same principal read every memory the space contains, now and later
http POST "$GOODMEM_REST_URL/v1/access-policy/grants" x-api-key:"$GOODMEM_API_KEY" <<JSON
{
"audience": { "principalId": "$PRINCIPAL_ID" },
"rule": { "operation": "READ_MEMORY", "selector": "DIRECT_MEMBERS_OF",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSONEXACT targets the space itself; DIRECT_MEMBERS_OF targets the memories directly contained in
it. Operations that act on the space (READ_SPACE, LIST_MEMORY, CREATE_MEMORY) take EXACT;
operations that act on individual memories (READ_MEMORY, DELETE_MEMORY) take
DIRECT_MEMBERS_OF. The operations reference
lists which selector fits which operation.
The space access commands accept only these two selectors, since both are anchored to the space
in the positional argument. Instance-wide ANY and owner-relative OWN grants exist too, but they
are instance policy rather than space policy; create them with the canonical form,
goodmem access-policy grant create, or over REST with a rule that omits assignedResource.
3. Retrieval Needs Two Gates
Semantic retrieval from a space checks two things independently: LIST_MEMORY with EXACT on the
space, and READ_MEMORY with DIRECT_MEMBERS_OF on the space. A principal holding only one of the
two gets PERMISSION_DENIED for the whole retrieval rather than partial results. This is why
SPACE_VIEWER carries three capabilities rather than one, and why the grant-based recipe for
read access is three grants:
goodmem space access grant create $SPACE_ID --principal $PRINCIPAL_ID \
--operation READ_SPACE --selector EXACT
goodmem space access grant create $SPACE_ID --principal $PRINCIPAL_ID \
--operation LIST_MEMORY --selector EXACT
goodmem space access grant create $SPACE_ID --principal $PRINCIPAL_ID \
--operation READ_MEMORY --selector DIRECT_MEMBERS_OFfor RULE in 'READ_SPACE EXACT' 'LIST_MEMORY EXACT' 'READ_MEMORY DIRECT_MEMBERS_OF'; do
set -- $RULE
curl -sS --json @- "$GOODMEM_REST_URL/v1/access-policy/grants" \
--header "x-api-key: $GOODMEM_API_KEY" <<JSON
{
"audience": { "principalId": "$PRINCIPAL_ID" },
"rule": { "operation": "$1", "selector": "$2",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON
donefor RULE in 'READ_SPACE EXACT' 'LIST_MEMORY EXACT' 'READ_MEMORY DIRECT_MEMBERS_OF'; do
set -- $RULE
http POST "$GOODMEM_REST_URL/v1/access-policy/grants" x-api-key:"$GOODMEM_API_KEY" <<JSON
{
"audience": { "principalId": "$PRINCIPAL_ID" },
"rule": { "operation": "$1", "selector": "$2",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON
doneA grant is unique per audience, operation, selector, and resource. If you already created the first
two of these in step 2, the server answers those with 409 (Authorization grant already exists)
and creates only LIST_MEMORY; nothing is duplicated.
If you are sharing for retrieval, assign SPACE_VIEWER and be done with it.
4. Open a Space to Everyone on the Instance
To make a space readable by every principal that can authenticate, grant to the all-authenticated
audience instead of a single principal. Over REST the audience is { "allAuthenticated": true } instead of a principalId:
goodmem space access grant create $SPACE_ID --all-authenticated \
--operation READ_SPACE --selector EXACT
goodmem space access grant create $SPACE_ID --all-authenticated \
--operation LIST_MEMORY --selector EXACT
goodmem space access grant create $SPACE_ID --all-authenticated \
--operation READ_MEMORY --selector DIRECT_MEMBERS_OFfor RULE in 'READ_SPACE EXACT' 'LIST_MEMORY EXACT' 'READ_MEMORY DIRECT_MEMBERS_OF'; do
set -- $RULE
curl -sS --json @- "$GOODMEM_REST_URL/v1/access-policy/grants" \
--header "x-api-key: $GOODMEM_API_KEY" <<JSON
{
"audience": { "allAuthenticated": true },
"rule": { "operation": "$1", "selector": "$2",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON
donefor RULE in 'READ_SPACE EXACT' 'LIST_MEMORY EXACT' 'READ_MEMORY DIRECT_MEMBERS_OF'; do
set -- $RULE
http POST "$GOODMEM_REST_URL/v1/access-policy/grants" x-api-key:"$GOODMEM_API_KEY" <<JSON
{
"audience": { "allAuthenticated": true },
"rule": { "operation": "$1", "selector": "$2",
"assignedResource": { "kind": "SPACE", "resourceId": "$SPACE_ID" } }
}
JSON
doneThe all-authenticated audience means every principal on this GoodMem instance that has successfully
authenticated. It does not expose the space to anonymous callers or to the internet, and it can
never receive MANAGE_ACCESS, so an open space cannot be used to bootstrap policy changes.
The audience can also receive write operations, such as CREATE_MEMORY with EXACT, if you want a
shared drop-box space. Grants to the audience accept only EXACT and DIRECT_MEMBERS_OF.
5. Review and Revoke
List what is attached to a space:
goodmem space access grant list $SPACE_ID
goodmem space access role list $SPACE_IDcurl -sS "$GOODMEM_REST_URL/v1/access-policy/grants?resourceKind=SPACE&resourceId=$SPACE_ID" \
--header "x-api-key: $GOODMEM_API_KEY"
curl -sS "$GOODMEM_REST_URL/v1/access-policy/role-assignments?resourceKind=SPACE&resourceId=$SPACE_ID" \
--header "x-api-key: $GOODMEM_API_KEY"http GET "$GOODMEM_REST_URL/v1/access-policy/grants" x-api-key:"$GOODMEM_API_KEY" \
resourceKind==SPACE resourceId=="$SPACE_ID"
http GET "$GOODMEM_REST_URL/v1/access-policy/role-assignments" x-api-key:"$GOODMEM_API_KEY" \
resourceKind==SPACE resourceId=="$SPACE_ID"Both return each record with its own UUID (grantId, roleAssignmentId). Revocation identifies a
policy record by that UUID rather than by the space:
goodmem access-policy grant revoke $GRANT_ID
goodmem access-policy role-assignment revoke $ROLE_ASSIGNMENT_IDcurl -sS -X DELETE "$GOODMEM_REST_URL/v1/access-policy/grants/$GRANT_ID" \
--header "x-api-key: $GOODMEM_API_KEY"
curl -sS -X DELETE "$GOODMEM_REST_URL/v1/access-policy/role-assignments/$ROLE_ASSIGNMENT_ID" \
--header "x-api-key: $GOODMEM_API_KEY"http DELETE "$GOODMEM_REST_URL/v1/access-policy/grants/$GRANT_ID" x-api-key:"$GOODMEM_API_KEY"
http DELETE "$GOODMEM_REST_URL/v1/access-policy/role-assignments/$ROLE_ASSIGNMENT_ID" x-api-key:"$GOODMEM_API_KEY"Revocation is permanent and takes effect immediately. The record stays visible to
list --include-revoked (includeRevoked=true) as history. To restore access later, create a new
grant or assignment.
Scoped API Keys Are a Second Filter
Sharing changes a principal's live authority. If the recipient authenticates with a scoped API
key, the key's immutable ceiling is checked as well, and the request succeeds only when both
allow it. Assigning SPACE_VIEWER to a service identity does nothing observable until that
identity uses a key whose ceiling covers the same operations. See
API Keys and Ceilings and
Issue Scoped API Keys.
For the reasoning behind roles, grants, selectors, and the two-gate rule, see Roles, Grants, and Selectors. For sharing with a fleet of agents, one space each, see Isolate Agents on a Shared Instance.