GoodMemGoodMem
How-To GuidesUsers and Access

Set Up a Service Identity

Give a production workload its own principal and scoped API keys, so credentials rotate without touching the workload's identity or access.

Set Up a Service Identity

A key issued to a person carries that person's authority. When the person changes teams, loses a permission, or leaves, every workload running on their key changes with them. A service identity avoids this: it is a durable principal that belongs to the workload itself. Grants attach to the identity, keys are issued against it, and keys can be rotated indefinitely without changing what the workload is or what it can do.

This guide sets up a retrieval service that reads from one space. The same steps work for an ingestion job, a batch pipeline, or anything else that runs unattended.

Before You Start

  • goodmem CLI installed and authenticated as a human user (GOODMEM_API_KEY set). Service identities are created by humans; a service key cannot create another service identity.
  • The UUID of the space your workload will read. Find it with goodmem space list --format json.
export SPACE_ID="70e025f6-76ca-4cbe-b8fc-7dab8e84590a"

1. Onboard in One Command

goodmem service-identity onboard \
  --display-name prod-search \
  --description "Semantic retrieval for the support portal" \
  --grant READ_SPACE:EXACT:SPACE:$SPACE_ID \
  --grant LIST_MEMORY:EXACT:SPACE:$SPACE_ID \
  --grant READ_MEMORY:DIRECT_MEMBERS_OF:SPACE:$SPACE_ID

Each --grant is one authorization rule in the form OPERATION:SELECTOR[:RESOURCE_KIND:RESOURCE_UUID]:

  • The operation comes from the operations catalog, for example READ_MEMORY or CREATE_MEMORY.
  • The selector says which resources the rule covers. EXACT names the one resource in the last two segments. DIRECT_MEMBERS_OF names everything directly inside a container, so READ_MEMORY:DIRECT_MEMBERS_OF:SPACE:$SPACE_ID covers every memory the space holds now or later. ANY and OWN also exist and take no resource segments.

The three rules above are what a retrieval service needs: retrieval checks LIST_MEMORY on each requested space and READ_MEMORY on its direct members, and READ_SPACE lets the service read the space's metadata. The key does not need EXECUTE_EMBEDDER; retrieval runs the space's configured embedders as part of the workflow.

The command makes three kinds of API call in order: it creates the identity, creates one direct grant per --grant with the new identity as the grantee, and issues one SCOPED API key whose ceiling repeats the same rules. On success it prints the IDs and the raw key:

Service Identity ID:  f031b49e-7a15-4c8c-b4e7-38f77225d2a6
API Key ID:           9be3170a-52cf-4c53-b9ba-2f2b4bd0aa46
Raw API Key:          gm_xxxxxxxxxxxxxxxxxxxxxxxx
Grant 1 ID:           1a2b3c4d-...
Grant 2 ID:           ...

The raw API key is shown once. Store it in your secret manager before closing the terminal; the server keeps only a verifier and cannot display the key again.

The steps are separate calls, so a failure partway through leaves the completed steps in place. The command reports each step's status and its UUID, which is enough to inspect what exists and finish the remaining steps by hand. Use --quiet to print only the raw key, or --format json for the full structured result.

2. Or Do It by Hand

The composite command is a convenience. Doing it by hand is useful when the grants and the key ceiling should differ; the example below sets up only the two retrieval gates.

Create the identity:

goodmem service-identity create --display-name prod-search
export SERVICE_ID="<the UUID from the output>"

Grant it authority, one rule per command:

goodmem access-policy grant create \
  --principal $SERVICE_ID \
  --operation LIST_MEMORY --selector EXACT \
  --resource-kind SPACE --resource-id $SPACE_ID

goodmem access-policy grant create \
  --principal $SERVICE_ID \
  --operation READ_MEMORY --selector DIRECT_MEMBERS_OF \
  --resource-kind SPACE --resource-id $SPACE_ID

Issue a scoped key:

goodmem apikey create \
  --subject $SERVICE_ID \
  --authority-mode scoped \
  --ceiling LIST_MEMORY:EXACT:SPACE:$SPACE_ID \
  --ceiling READ_MEMORY:DIRECT_MEMBERS_OF:SPACE:$SPACE_ID

Order matters. At issuance, every ceiling rule must already be covered both by the identity's live authority and by yours, so a ceiling for authority the identity does not yet have is rejected. Create the grants first. Issuing a key whose subject is a service identity also requires MANAGE_ACCESS on that identity; as its owner, you have that automatically.

Service keys are always scoped — --authority-mode inherit-subject is only valid for a key you issue to yourself. A scoped key's ceiling is fixed at issuance, so grants the identity receives later do not flow into existing keys. An old credential sitting in a config file somewhere never quietly gains the authority its subject accumulates over time. See API Keys and Ceilings for how the two-sided check works.

3. Rotate Keys

Rotation issues a second key for the same subject, deploys it, and revokes the first:

goodmem apikey create \
  --subject $SERVICE_ID \
  --authority-mode scoped \
  --ceiling LIST_MEMORY:EXACT:SPACE:$SPACE_ID \
  --ceiling READ_MEMORY:DIRECT_MEMBERS_OF:SPACE:$SPACE_ID

# ...deploy the new key, then:
goodmem apikey delete $OLD_KEY_ID

Deletion revokes the key permanently; the row is retained for audit and shows up in goodmem apikey list as inactive. The identity, its grants, and its ownership are untouched throughout.

For scheduled rotation, give each key a validity window at issuance with --valid-from and --expires-at (RFC3339 timestamps). A key works from valid-from inclusive to expires-at exclusive, and neither bound can be changed later — replacing the key is the only way to extend it.

4. When the Owner Leaves

Deleting a human user does nothing to the service identities they created or own. The identity stays active and its keys keep authenticating. What the departed human leaves behind is administrative ownership, which someone else should pick up:

goodmem service-identity transfer-ownership $SERVICE_ID \
  --new-owner 44e8175d-c57a-4cf2-8f65-a20c05ea2250

The current owner, the instance owner, or an instance administrator can run this. The transfer changes the owner and nothing else — grants, roles, keys, and the identity's own authority are all preserved.

5. Housekeeping

goodmem service-identity list --label environment=production
goodmem service-identity update $SERVICE_ID --description "Retired from the support portal"
goodmem service-identity delete $SERVICE_ID

Deletion is permanent. The identity becomes a tombstone, every key whose subject it is stops authenticating, and the UUID is never reused. Grants, role assignments, and audit history remain stored as historical facts. There is no undelete; for a workload that might come back, revoke its keys instead and leave the identity in place.

See Also