GoodMemGoodMem
How-To GuidesUsers and Access

Upgrade to the New Authorization Model

What changes for existing deployments when the new authorization model arrives, and what to do about each change.

Upgrade to the New Authorization Model

This release replaces GoodMem's flat permission model with principals, roles, grants, and API-key ceilings. The security model overview explains the new system on its own terms. This guide covers the other half: what an existing deployment observes after upgrading, and what to do about it. Items appear roughly in order of how likely they are to affect you.

1. API Key Responses Have a New Shape

The user_id field on API keys is gone. A key now records which principal it authenticates as and which principal administers it, and these can differ:

Old fieldNew fields
userIdsubjectPrincipalId (who the key authenticates as), ownerPrincipalId (who administers the key)
authorityMode (INHERIT_SUBJECT or SCOPED), ceiling, validFrom, revokedAt, revokedById

Client code that reads userId from key responses breaks. For a personal key, subjectPrincipalId carries the value userId used to hold. The distinction between subject and owner matters once service identities enter the picture; API Keys and Ceilings explains the split.

2. Deactivating a Key Is Now Permanent

Setting a key's status to INACTIVE revokes it. Setting it back to ACTIVE fails with gRPC FAILED_PRECONDITION, HTTP 412. DELETE /v1/apikeys/{id} also revokes rather than deleting: the row survives with its audit history, the response is still 204, and the revoked key continues to appear in listings as inactive.

Any automation that toggles keys off and on — a pause switch, a scheduled disable — needs a new design. The supported pattern is rotation: issue a replacement key, move the workload over, revoke the old one. Revocation is the end of a key's life, and the API now says so.

3. Keys That Worked Yesterday Return 401

Authentication now enforces the full set of validity rules on every request: the key must be unrevoked, the current time must fall inside [validFrom, expiresAt), and the key's subject must be an active principal. Keys that slipped through looser historical checks fail closed with 401. A key's reported status is likewise derived from the clock and revocation state rather than a stored value.

When a key starts returning 401 after the upgrade, check in this order:

  1. revokedAt — the key was revoked, possibly by an old "deactivate" step (section 2).
  2. validFrom and expiresAt — the clock is outside the validity window.
  3. The subject — the user or service identity the key authenticates as has been deleted (section 4).
goodmem apikey list --format json

The fix in every case is a replacement key. There is no repair operation for an ineligible key.

4. Deleted Users Return 404

Deleted users are permanent tombstones. /v1/users/me, /v1/users/{id}, and /v1/users/email/{email} return 404 for a deleted user, and every API key whose subject is a deleted user stops authenticating. The keys themselves are not marked revoked; they no longer resolve to an active principal.

Separately, GetUser now takes exactly one selector — UUID, email, or username. The old request shape allowed several at once with a priority order; sending more than one is now an error.

5. Permission Names Have Changed

The *_OWN and *_ANY permission strings are gone. Authority is now expressed as an operation plus a selector:

Old nameOperationSelector
LIST_SPACE_ANYLIST_SPACEANY
CREATE_APIKEY_OWNCREATE_API_KEYOWN

Two selectors have no old equivalent: EXACT (one named resource) and DIRECT_MEMBERS_OF (the direct members of one container, such as the memories in a space). Scripts, saved policies, and internal documentation that mention the old names need the two-part vocabulary. The operations and selectors reference has the complete catalog.

6. New Retryable Errors

PostgreSQL serialization and deadlock failures now surface as gRPC ABORTED, HTTP 409, instead of a generic 500. These are safe to retry: the transaction did not commit. Clients that treated every 409 as a duplicate-resource conflict should distinguish the two cases; ALREADY_EXISTS also maps to 409 but retrying it will not change the answer.

7. Regenerate Your Clients

The OpenAPI specification, the generated SDKs, and the CLI all change shape in this release. Generated clients from earlier releases still carry removed fields such as userId and lack the new key fields, so they fail in the ways described above even when your own code is correct. Upgrade the SDKs and CLI to the versions matching your server before debugging anything else.

8. What Does Not Change

  • Existing personal API keys issued before the upgrade keep working, provided they are unrevoked and their subject is active. They behave as inheriting keys: they carry their user's current authority.
  • Spaces, memories, embedders, rerankers, and LLMs carry over untouched, along with their ownership.
  • The bootstrap key created at install time is an ordinary inheriting key for the instance owner and continues to work.
  • The x-api-key header, the CLI profile format, and the GOODMEM_API_KEY environment variable are unchanged.

9. Suggested Order of Operations

  1. Find and fix any automation that toggles or deletes keys (section 2) and any code reading userId from key responses (section 1).
  2. Regenerate SDK clients and upgrade CLIs (section 7).
  3. Translate any remaining *_OWN / *_ANY references to operations and selectors (section 5).
  4. Add retry handling for ABORTED / 409 where your clients mutate data (section 6).

After that, the new machinery — roles, scoped keys, service identities — is available when you want it, and nothing requires you to adopt it on day one.