Service Identity
Service Identity service API reference
Services
ServiceIdentityService Service
Manages workload service identities independently from credentials, grants, and roles.
Authentication: Every RPC requires gRPC metadata authorization: Bearer <api-key>.
Authorization Model:
- Each RPC evaluates a typed operation against the target service identity or instance.
- Role names are not interpreted by this service.
- Creation is restricted to
HUMANrequest principals, including humans using an API key.
Global Errors: All RPCs may return DEADLINE_EXCEEDED, CANCELLED, UNAVAILABLE,
RESOURCE_EXHAUSTED, or INTERNAL in addition to their operation-specific errors.
CreateServiceIdentity
Creates one service identity owned and created by the authenticated human request principal.
| Type | |
|---|---|
| Request | goodmem.v1.CreateServiceIdentityRequest |
| Response | goodmem.v1.ServiceIdentity |
Auth: gRPC metadata authorization: Bearer <api-key>
Authorization Required: CREATE_SERVICE_IDENTITY on the proposed identity, with the
authenticated human principal as its owner.
Request Behavior:
- The request principal must be
HUMAN, including a human authenticated through an API key. display_nameis required, nonblank, and globally unique, including among tombstones.descriptionis optional free-form text. The service applies no format or length rule beyond protobuf transport validity; an empty present value is treated as absent during creation.labelsare optional workload metadata and follow the documented label constraints.- The server generates
service_identity_idwhen the caller omits it. - The initial API does not accept an owner override; use the transfer RPC after creation.
Response: The newly created ServiceIdentity, including ownership, lifecycle, and audit
metadata. No credential material is returned.
Side Effects:
- Atomically creates the durable
SERVICEprincipal and service-identity profile. - Sets the authenticated human as both immutable creator and initial administrative owner.
- Does not create an API key, role assignment, or direct grant.
Error Codes:
UNAUTHENTICATED: Missing or invalid authentication.INVALID_ARGUMENT: Malformed UUID, blank or overlong display name, or invalid labels.PERMISSION_DENIED: Request principal is notHUMANor lacks creation authority.ALREADY_EXISTS: The UUID or display name is already occupied.INTERNAL: Unexpected server or database failure.
Idempotency: Not inherently idempotent. When a caller supplies service_identity_id, a
retry after successful creation returns ALREADY_EXISTS because the UUID remains occupied.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer <api-key>' \
-d '{
"display_name": "production-search",
"description": "Production semantic-search workload",
"labels": { "environment": "production" }
}' \
localhost:8080 goodmem.v1.ServiceIdentityService/CreateServiceIdentityGetServiceIdentity
Retrieves one service identity by UUID.
| Type | |
|---|---|
| Request | goodmem.v1.GetServiceIdentityRequest |
| Response | goodmem.v1.ServiceIdentity |
Auth: gRPC metadata authorization: Bearer <api-key>
Authorization Required: READ_SERVICE_IDENTITY on the resolved service identity.
Request (Lookup Logic):
- Resolves the exact
service_identity_idsupplied by the caller. - In protobuf JSON and
grpcurl, the bytes-valued ID is base64-encoded. - Normal lookup excludes permanent tombstones.
include_deleted=truepermits a tombstone to participate in lookup but grants no authority.- Existence is resolved before authorization, preserving
NOT_FOUNDbeforePERMISSION_DENIED.
Response: The resolved ServiceIdentity in the requested lifecycle view.
Side Effects: None; this is a read-only operation.
Error Codes:
UNAUTHENTICATED: Missing or invalid authentication.INVALID_ARGUMENT: Malformed service-identity UUID.NOT_FOUND: No identity exists in the requested lifecycle view.PERMISSION_DENIED: Caller lacksREAD_SERVICE_IDENTITYon the resolved identity.INTERNAL: Unexpected server or database failure.
Idempotency: Read-only and safe to retry.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer <api-key>' \
-d '{ "service_identity_id": "BASE64_ENCODED_UUID" }' \
localhost:8080 goodmem.v1.ServiceIdentityService/GetServiceIdentityListServiceIdentities
Lists an authorization-filtered page of service identities.
| Type | |
|---|---|
| Request | goodmem.v1.ListServiceIdentitiesRequest |
| Response | goodmem.v1.ListServiceIdentitiesResponse |
Auth: gRPC metadata authorization: Bearer <api-key>
Authorization Required:
LIST_SERVICE_IDENTITYon the singleton GoodMem instance; andREAD_SERVICE_IDENTITYon every identity returned in the page.
Request Behavior:
owner_principal_idoptionally restricts results to one administrative owner.label_selectorsare combined with logical AND.include_deleted=trueadds tombstones to the candidate set but bypasses no authorization.max_resultsdefaults to 50 and may not exceed 1,000.next_tokenis opaque and bound to the caller, authentication context, and stable filters.- Filtering, authorization, and keyset pagination execute in PostgreSQL.
Response: A ListServiceIdentitiesResponse ordered by ascending creation time and UUID.
next_token is absent after the final page.
Side Effects: None; this is a read-only operation.
Error Codes:
UNAUTHENTICATED: Missing or invalid authentication.INVALID_ARGUMENT: Invalid owner/filter UUID, labels, page size, or continuation token.PERMISSION_DENIED: Caller lacks the instance-level collection gate.INTERNAL: Unexpected server or database failure.
Idempotency: Read-only and safe to retry. Concurrent mutations may change later pages.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer <api-key>' \
-d '{ "label_selectors": { "environment": "production" }, "max_results": 50 }' \
localhost:8080 goodmem.v1.ServiceIdentityService/ListServiceIdentitiesUpdateServiceIdentity
Updates mutable profile fields or labels of one active service identity.
| Type | |
|---|---|
| Request | goodmem.v1.UpdateServiceIdentityRequest |
| Response | goodmem.v1.ServiceIdentity |
Auth: gRPC metadata authorization: Bearer <api-key>
Authorization Required: UPDATE_SERVICE_IDENTITY against the identity's current owner.
Request Behavior:
- At least one mutable field or label strategy must be present.
- A present blank
display_nameis invalid. - A present empty
descriptionclears that optional field; other values are free-form text with no application-level format or length rule. replace_labelsreplaces the complete label map;merge_labelsupserts supplied entries.- The two label strategies are mutually exclusive.
- In protobuf JSON and
grpcurl, the bytes-valuedservice_identity_idis base64-encoded. - Ownership is immutable through this RPC; use
TransferServiceIdentityOwnershipinstead. - The final write rechecks authorization and active lifecycle state.
Response: The updated ServiceIdentity, including refreshed update-audit metadata.
Side Effects: Updates selected profile fields, labels, and update-audit metadata.
Error Codes (in precedence order):
UNAUTHENTICATED: Missing or invalid authentication.INVALID_ARGUMENT: Malformed UUID, invalid/no-op mutation, or invalid labels.NOT_FOUND: The service-identity UUID does not exist.PERMISSION_DENIED: Caller lacksUPDATE_SERVICE_IDENTITYon the identity.FAILED_PRECONDITION: The service identity is permanently deleted.ALREADY_EXISTS: The replacement display name is already occupied.ABORTED: A concurrent lifecycle or authorization change invalidated the final write.INTERNAL: Unexpected server or database failure.
Idempotency: Repeating the same explicit values preserves the same profile state, although each successful call advances update-audit metadata.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer <api-key>' \
-d '{
"service_identity_id": "BASE64_ENCODED_UUID",
"description": "Updated production workload",
"merge_labels": { "labels": { "team": "search" } }
}' \
localhost:8080 goodmem.v1.ServiceIdentityService/UpdateServiceIdentityDeleteServiceIdentity
Permanently soft-deletes one service identity.
| Type | |
|---|---|
| Request | goodmem.v1.DeleteServiceIdentityRequest |
| Response | google.protobuf.Empty |
Auth: gRPC metadata authorization: Bearer <api-key>
Authorization Required: DELETE_SERVICE_IDENTITY against the identity's current owner.
Request Behavior:
- The target must be an existing service principal.
- In protobuf JSON and
grpcurl, the bytes-valuedservice_identity_idis base64-encoded. - Deleting an already deleted service identity succeeds without changing its original tombstone or audit provenance.
Response: An empty protobuf message after the tombstone is committed.
Side Effects:
- Atomically records the permanent principal tombstone and deleting audit actor.
- Preserves the profile, credentials, roles, grants, and owned-resource columns for history.
- Authentication rejects subject API keys after deletion without marking the keys revoked.
Error Codes (in precedence order):
UNAUTHENTICATED: Missing or invalid authentication.INVALID_ARGUMENT: Malformed service-identity UUID.NOT_FOUND: The service-identity UUID does not exist.PERMISSION_DENIED: Caller lacksDELETE_SERVICE_IDENTITYon the identity.ABORTED: A concurrent lifecycle or authorization change invalidated the final write.INTERNAL: Unexpected server or database failure.
Idempotency: Authorized retries succeed without changing the original tombstone or its audit provenance.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer <api-key>' \
-d '{ "service_identity_id": "BASE64_ENCODED_UUID" }' \
localhost:8080 goodmem.v1.ServiceIdentityService/DeleteServiceIdentityTransferServiceIdentityOwnership
Transfers administrative ownership to a different active HUMAN or SERVICE principal.
| Type | |
|---|---|
| Request | goodmem.v1.TransferServiceIdentityOwnershipRequest |
| Response | goodmem.v1.TransferServiceIdentityOwnershipResponse |
Auth: gRPC metadata authorization: Bearer <api-key>
Authorization Required: Transfer authority derived from at least one of:
- ownership of the active service identity;
- ownership of the singleton GoodMem instance; or
TRANSFER_RESOURCE_OWNERSHIPauthority covering the service identity.
Request and Validation Order:
- Resolves the service identity before authorization, preserving
NOT_FOUNDbeforePERMISSION_DENIEDfor the target. - Authorizes transfer against the identity's current owner.
- Resolves the destination only after target authorization to avoid disclosing principal data.
- Requires a different, active
HUMANorSERVICEdestination principal. - A service identity cannot be its own administrative owner.
- In protobuf JSON and
grpcurl, both bytes-valued UUID fields are base64-encoded. - Rechecks target lifecycle, ownership, and authorization in the final write.
Response: A TransferServiceIdentityOwnershipResponse containing the identity with its new
administrative owner and refreshed update-audit metadata.
Side Effects:
- Changes only
owner_principal_idand update-audit fields. - Does not change the identity's subject UUID, creator, credentials, roles, grants, or lifecycle.
Error Codes:
UNAUTHENTICATED: Missing or invalid authentication.INVALID_ARGUMENT: Malformed target or destination UUID.NOT_FOUND: Target does not exist, or destination does not exist after target authorization.PERMISSION_DENIED: Caller lacks transfer authority on the service identity.FAILED_PRECONDITION: Target/destination is deleted, destination already owns the target, or the service identity itself was supplied as the destination.ABORTED: A concurrent mutation invalidated the transfer transaction; the request may retry.INTERNAL: Unexpected server or database failure.
Idempotency: A successful transfer changes ownership once. Repeating it with the same
destination returns FAILED_PRECONDITION because that principal is already the owner.
Examples:
grpcurl -plaintext \
-H 'authorization: Bearer <api-key>' \
-d '{
"service_identity_id": "BASE64_ENCODED_SERVICE_IDENTITY_UUID",
"new_owner_principal_id": "BASE64_ENCODED_PRINCIPAL_UUID"
}' \
localhost:8080 \
goodmem.v1.ServiceIdentityService/TransferServiceIdentityOwnershipMessages
ServiceIdentity
A production workload backed by a durable SERVICE principal.
A service identity has one mutable administrative owner, but its authenticated subject UUID and immutable creator principal never change. Credentials, grants, and roles are separate resources and are never created or rewritten by this service. Deletion is a permanent tombstone transition.
All fields are OUTPUT_ONLY. Timestamps are UTC. Audit actor identifiers may name either a
principal or the exact API key used for the mutation.
| Field | Type | Description |
|---|---|---|
service_identity_id | bytes | OUTPUT_ONLY; UUID (16 bytes), immutable and permanently occupied after creation. |
display_name | string | OUTPUT_ONLY; globally unique, nonblank display name. Uniqueness includes tombstones. |
description | string | OUTPUT_ONLY; optional operator description. |
owner_principal_id | bytes | OUTPUT_ONLY; UUID (16 bytes) of the current owner; changed only by the transfer RPC. |
creator_principal_id | bytes | OUTPUT_ONLY; UUID (16 bytes) of the HUMAN creator principal; immutable. |
labels | ...v1.ServiceIdentity.LabelsEntry | OUTPUT_ONLY; mutable labels; at most 20 entries with keys matching [a-z0-9._-]. |
created_at | google.protobuf.Timestamp | OUTPUT_ONLY; creation timestamp. |
updated_at | google.protobuf.Timestamp | OUTPUT_ONLY; timestamp of the most recent profile, ownership, or lifecycle mutation. |
created_by_id | bytes | OUTPUT_ONLY; exact audit actor UUID (16 bytes) that created the identity. |
updated_by_id | bytes | OUTPUT_ONLY; exact audit actor UUID (16 bytes) responsible for the most recent mutation. |
deleted_at | google.protobuf.Timestamp | OUTPUT_ONLY; permanent soft-deletion timestamp; absent while active. |
deleted_by_id | bytes | OUTPUT_ONLY; exact actor UUID (16 bytes) that deleted the identity; absent while active. |
ServiceIdentity.LabelsEntry
| Field | Type | Description |
|---|---|---|
key | string | |
value | string |
CreateServiceIdentityRequest
Request to create a service identity without implicit security side effects.
| Field | Type | Description |
|---|---|---|
service_identity_id | bytes | Optional client-provided UUID (16 bytes); the server generates one when absent. |
display_name | string | Required globally unique, nonblank display name. |
description | string | Optional free-form operator description; an empty present value is treated as absent. |
labels | ...iceIdentityRequest.LabelsEntry | Optional labels; at most 20 entries, keys/values at most 255 chars, keys [a-z0-9._-]. |
CreateServiceIdentityRequest.LabelsEntry
| Field | Type | Description |
|---|---|---|
key | string | |
value | string |
GetServiceIdentityRequest
Request to get one service identity by UUID.
| Field | Type | Description |
|---|---|---|
service_identity_id | bytes | Required service-identity UUID (16 bytes). |
include_deleted | bool | Include a permanent tombstone in the lifecycle view; does not bypass authorization. |
ListServiceIdentitiesRequest
Request for one stable, authorization-filtered service-identity page.
| Field | Type | Description |
|---|---|---|
owner_principal_id | bytes | Optional exact administrative-owner UUID (16 bytes). |
label_selectors | ...iesRequest.LabelSelectorsEntry | Conjunction of exact key/value matches against principal labels. |
include_deleted | bool | Include tombstones in the candidate lifecycle view; does not bypass authorization. |
max_results | int32 | Page size; defaults to 50 and must be between 1 and 1000 when present. |
next_token | string | Opaque continuation token returned by the preceding page. |
ListServiceIdentitiesRequest.LabelSelectorsEntry
| Field | Type | Description |
|---|---|---|
key | string | |
value | string |
ListServiceIdentitiesResponse
One stable page of readable service identities. All fields are OUTPUT_ONLY.
| Field | Type | Description |
|---|---|---|
service_identities | goodmem.v1.ServiceIdentity | OUTPUT_ONLY; identities ordered by creation timestamp and UUID using stable keyset ordering. |
next_token | string | OUTPUT_ONLY; opaque continuation token; absent after the final page. |
ListServiceIdentitiesNextPageToken
Internal cursor encoded by the server; clients must treat public next_token strings as opaque.
| Field | Type | Description |
|---|---|---|
requestor_id | bytes | Authenticated request-principal UUID bound to the first page. |
authenticating_api_key_id | bytes | Exact authenticating API-key UUID, when API-key authentication was used. |
owner_principal_id | bytes | Administrative-owner filter bound to the first page. |
label_selectors | ...tPageToken.LabelSelectorsEntry | Label filters bound to the first page. |
include_deleted | bool | Lifecycle filter bound to the first page. |
last_created_at | google.protobuf.Timestamp | Creation timestamp of the final row returned by the preceding page. |
last_service_identity_id | bytes | Service-identity UUID of the final row returned by the preceding page. |
ListServiceIdentitiesNextPageToken.LabelSelectorsEntry
| Field | Type | Description |
|---|---|---|
key | string | |
value | string |
UpdateServiceIdentityRequest
Request to update mutable fields of one active service identity.
| Field | Type | Description |
|---|---|---|
service_identity_id | bytes | Required service-identity UUID (16 bytes). |
display_name | string | Optional replacement display name; a present empty or blank value is invalid. |
description | string | Optional replacement description; a present empty value clears the description. |
replace_labels | goodmem.v1.StringMap | Replace all labels; an empty map clears them. |
merge_labels | goodmem.v1.StringMap | Upsert entries while preserving unmentioned labels; the final map must satisfy label limits. |
DeleteServiceIdentityRequest
Request to permanently soft-delete a service identity.
| Field | Type | Description |
|---|---|---|
service_identity_id | bytes | Required service-identity UUID (16 bytes). |
TransferServiceIdentityOwnershipRequest
Request to transfer service-identity administration to another active principal.
| Field | Type | Description |
|---|---|---|
service_identity_id | bytes | Required existing service-identity UUID (16 bytes). |
new_owner_principal_id | bytes | Required different, active HUMAN or SERVICE principal UUID (16 bytes); never the target itself. |
TransferServiceIdentityOwnershipResponse
Result of one successful service-identity ownership transfer.
| Field | Type | Description |
|---|---|---|
service_identity | goodmem.v1.ServiceIdentity | OUTPUT_ONLY; updated service identity with the new administrative owner. |