Managing service metadata through the REST API#

Permission Service tracks service metadata — the catalog of services that integrate with authorization, the actions each service exposes, and the resource types those actions operate on. This catalog is exposed as a REST API under /v1beta/services/ so that clients and management tools can discover and update it at runtime.

Metadata is primarily reference information used to understand other service operations. Callers rarely need it to evaluate a request; instead, the Permission UI uses it to drive autocomplete when composing Cedar policies, so policy authors can pick a known service, pick one of its registered actions, and pick one of its registered resource types instead of typing identifiers by hand. The API described on this page is what the UI (and any equivalent tooling) calls to populate those suggestions.

A small number of metadata fields are consumed by the service itself at authorization time:

  • id_claim on a ServiceMeta selects which claim from the caller’s token is used as the principal id for requests that target that service. See idClaim for principals below.

  • evaluation_priority on a ResourceTypeMeta controls how competing policies are combined for resources of that type. See Evaluation priority for resources below.

All other metadata — the list of services, the names of actions, the set of resource types — is advisory: it is not required for a policy to be stored or evaluated, and the service does not reject unknown services, actions, or resource types at authorization time.

For how policies are written and evaluated, see Writing Cedar policies for Permission Service. For how metadata is seeded from Helm or YAML at startup, see Deployment and Database configuration.

Overview#

The metadata API is a CRUD surface organized around three nested records — services, their actions, and their resource types:

Method

Path

Purpose

GET

/v1beta/services/

List all services and their ServiceMeta.

GET

/v1beta/services/{service}/

Fetch ServiceMeta for one service.

PUT

/v1beta/services/{service}/

Create or update ServiceMeta for one service.

DELETE

/v1beta/services/{service}/

Remove ServiceMeta for one service.

GET

/v1beta/services/{service}/actions/

List registered actions for a service.

PUT

/v1beta/services/{service}/actions/

Replace the full set of actions for a service.

PUT

/v1beta/services/{service}/actions/{action}/

Add or update a single action.

DELETE

/v1beta/services/{service}/actions/{action}/

Remove a single action.

GET

/v1beta/services/{service}/resource-types/

List registered resource types for a service.

PUT

/v1beta/services/{service}/resource-types/

Replace the full set of resource types for a service.

GET

/v1beta/services/{service}/resource-types/{type}/

Fetch ResourceTypeMeta for one resource type.

PUT

/v1beta/services/{service}/resource-types/{type}/

Create or update ResourceTypeMeta for one resource type.

DELETE

/v1beta/services/{service}/resource-types/{type}/

Remove ResourceTypeMeta for one resource type.

Key behaviors that apply to all endpoints:

  • Storage backend. Write operations require Postgres mode. In config-file mode the storage is read-only and write endpoints return 501 Not Implemented. Read endpoints are available in both modes. See Deployment for how to select a backend.

  • Path parameters win over the body. For PUT /v1beta/services/{service}/, PUT /v1beta/services/{service}/actions/, and PUT /v1beta/services/{service}/resource-types/{type}/, the service (and, where applicable, type) value is taken from the URL; any matching field in the JSON body is overwritten before the record is stored. Clients can safely omit those fields.

  • Cache invalidation. Successful writes invalidate the in-memory metadata and evaluation caches for the affected service so that subsequent reads and authorization requests see the new state immediately.

Seeding metadata at deployment time#

Instead of (or in addition to) calling this API at runtime, metadata can be populated at deployment time through the Helm value database.init.services. Each entry describes one service and optionally its principal idClaim, its registered actions, and its resource types with evaluation priorities. The chart renders the list into a ConfigMap and mounts it at /etc/permission-config/ so the service can load it on startup. See also Metadata file format.

How the seed is applied depends on the storage backend:

  • Config-file mode — the rendered file is the read-only metadata store; the only way to change metadata in this mode is to update the Helm value and redeploy. The service reloads the file automatically when its modification time changes.

  • Postgres mode — seeded entries are written into the database on startup, and can be changed afterwards through the REST API described on this page.

The example below seeds the four standard Omniverse services — storage-service, event-consumer-service, event-aggregation-service, and userinfo — with the actions and resource types listed in Actions and resources for Omniverse services:

database:
  init:
    services:
      - name: "storage-service"
        principal:
          idClaim: "sub"
        actions:
          - "read"
          - "write"
        resourceTypes:
          - type: "object"
            evaluationPriority: "permit"
          - type: "folder"
            evaluationPriority: "permit"

      - name: "event-consumer-service"
        actions:
          - "consume-all-storage-create-events"
          - "consume-all-storage-delete-events"
          - "consume-durable-queues"
          - "create-durable-queues"
          - "delete-durable-queues"

      - name: "event-aggregation-service"
        actions:
          - "publish-event"
        resourceTypes:
          - type: "EventType"
            evaluationPriority: "forbid"

      - name: "userinfo"
        actions:
          - "list-users"
          - "get-user"
          - "list-user-groups"
          - "get-user-group"
          - "list-groups"
          - "get-group"
          - "list-group-members"
          - "get-group-member"
        resourceTypes:
          - type: "User"
          - type: "Group"

A few notes on the schema:

  • principal.idClaim is optional. When it is omitted, the service falls back to the deployment-wide PRINCIPAL_ID_CLAIM (default sub). See idClaim for principals.

  • actions are plain names; they are referenced from Cedar policies as Action::"<service>:<name>".

  • resourceTypes[].evaluationPriority is optional and defaults to "forbid". See Evaluation priority for resources.

  • A service that registers actions only (like event-consumer-service above) simply omits resourceTypes; Cedar policies for those actions leave the resource slot unconstrained.

Authentication and authorization#

When authentication is enabled on the service, callers must include a valid bearer token:

Authorization: Bearer <token>

Requests without a token receive 401 Unauthorized. When authentication is disabled all endpoints are reachable without a token.

Every endpoint on this page is gated by a single meta-permission:

  • Meta — reading or writing metadata requires the caller to be authorized for Action::"permissions:meta". This gate is evaluated using the service’s own Cedar engine against the currently stored policies.

Callers who lack this meta-permission receive 403 Forbidden. When the service bootstraps an empty database, a typical set of admin policies is seeded through database.init.policies (see Deployment) so that administrators can manage metadata through this API.

Records#

Three record shapes appear in request and response bodies.

ServiceMeta — one per service:

{
  "service": "storage-service",
  "id_claim": "email"
}

Field

Type

Description

service

string

Service name. Matches the service segment in the URL.

id_claim

string

Optional. Claim from the caller’s token used as the principal id for requests targeting this service. See idClaim for principals. Empty string means “unset”; accepts the alias idClaim.

Action — one per registered action:

{
  "name": "read",
  "service": "storage-service"
}

Field

Type

Description

name

string

Action name, up to 255 characters. Used as the <name> portion of Action::"<service>:<name>" in Cedar policies.

service

string

Service the action belongs to. Always matches the service segment in the URL.

ResourceTypeMeta — one per resource type:

{
  "service": "storage-service",
  "type": "object",
  "evaluation_priority": "forbid"
}

Field

Type

Description

service

string

Service the resource type belongs to. Matches the service segment in the URL.

type

string

Resource type name. Used as the Cedar entity type (for example object::"<id>").

evaluation_priority

string

Optional. One of "forbid" (default) or "permit". Controls how competing policies are combined for this resource type. See Evaluation priority for resources. Accepts the alias evaluationPriority.

GET /v1beta/services/ — list services#

Returns every service registered in the database, each as a ServiceMeta record.

Request#

GET /v1beta/services/ HTTP/1.1
Host: permission-service.storage-apis.svc.cluster.local:3000
Authorization: Bearer <token>

Responses#

Status

Body

Meaning

200 OK

Vec<ServiceMeta>

Services returned; empty array when none are registered.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while reading metadata.

501 Not Implemented

error message (string)

Storage backend does not support this operation.

GET /v1beta/services/{service}/ — fetch service metadata#

Returns the ServiceMeta for a single service.

Path parameters#

Name

Type

Description

service

string

Service name.

Responses#

Status

Body

Meaning

200 OK

ServiceMeta

Service found.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

404 Not Found

error message (string)

No service with that name is registered.

500 Internal Server Error

error message (string)

Storage failure while reading metadata.

501 Not Implemented

error message (string)

Storage backend does not support this operation.

PUT /v1beta/services/{service}/ — update service metadata#

Creates the service record if it does not exist, or updates it in place. The service field of the payload is overwritten with the URL path segment, so the body effectively only needs to carry id_claim.

Path parameters#

Name

Type

Description

service

string

Service name.

Request#

PUT /v1beta/services/storage-service/ HTTP/1.1
Host: permission-service.storage-apis.svc.cluster.local:3000
Authorization: Bearer <token>
Content-Type: application/json

{
  "id_claim": "email"
}

Responses#

Status

Body

Meaning

200 OK

ServiceMeta

Metadata stored; the response echoes the record as persisted.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

422 Unprocessable Entity

error message (string)

JSON body failed to deserialize.

500 Internal Server Error

error message (string)

Storage failure while writing metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

DELETE /v1beta/services/{service}/ — delete service metadata#

Removes the service record, along with its actions and resource types in storage backends that cascade deletes.

Path parameters#

Name

Type

Description

service

string

Service name.

Responses#

Status

Body

Meaning

204 No Content

Service removed, or no service with that name existed.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while deleting metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

GET /v1beta/services/{service}/actions/ — list actions#

Returns every action registered for the service.

Path parameters#

Name

Type

Description

service

string

Service name.

Responses#

Status

Body

Meaning

200 OK

Vec<Action>

Actions returned; empty array when none are registered.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while reading metadata.

501 Not Implemented

error message (string)

Storage backend does not support this operation.

PUT /v1beta/services/{service}/actions/ — replace all actions#

Replaces the complete set of actions registered for the service. All existing actions for {service} are deleted and replaced with the supplied list. The service field on every item in the body is overwritten with the URL path segment, so clients can omit it.

An empty array is accepted and clears all actions for the service.

Path parameters#

Name

Type

Description

service

string

Service name.

Request#

PUT /v1beta/services/storage-service/actions/ HTTP/1.1
Host: permission-service.storage-apis.svc.cluster.local:3000
Authorization: Bearer <token>
Content-Type: application/json

[
  { "name": "read" },
  { "name": "write" }
]

Responses#

Status

Body

Meaning

200 OK

Actions replaced.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

422 Unprocessable Entity

error message (string)

JSON body failed to deserialize or an item failed per-field validation (for example, name length or pattern).

500 Internal Server Error

error message (string)

Storage failure while writing metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

PUT /v1beta/services/{service}/actions/{action}/ — add or update a single action#

Registers a single action for the service, or leaves it in place if it already exists. The request has no body; the action name is taken from the URL.

Path parameters#

Name

Type

Description

service

string

Service name.

action

string

Action name.

Responses#

Status

Body

Meaning

200 OK

Action

Action stored; the response echoes the record.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while writing metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

DELETE /v1beta/services/{service}/actions/{action}/ — delete an action#

Removes a single action from the service.

Path parameters#

Name

Type

Description

service

string

Service name.

action

string

Action name.

Responses#

Status

Body

Meaning

204 No Content

Action removed, or no such action existed.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while deleting metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

GET /v1beta/services/{service}/resource-types/ — list resource types#

Returns every resource type registered for the service.

Path parameters#

Name

Type

Description

service

string

Service name.

Responses#

Status

Body

Meaning

200 OK

Vec<ResourceTypeMeta>

Resource types returned; empty array when none are registered.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while reading metadata.

501 Not Implemented

error message (string)

Storage backend does not support this operation.

PUT /v1beta/services/{service}/resource-types/ — replace all resource types#

Replaces the complete set of resource types registered for the service. All existing resource types for {service} are deleted and replaced with the supplied list. The service field on every item in the body is overwritten with the URL path segment.

An empty array is accepted and clears all resource types for the service.

Path parameters#

Name

Type

Description

service

string

Service name.

Request#

PUT /v1beta/services/storage-service/resource-types/ HTTP/1.1
Host: permission-service.storage-apis.svc.cluster.local:3000
Authorization: Bearer <token>
Content-Type: application/json

[
  { "type": "object",  "evaluation_priority": "forbid" },
  { "type": "folder",  "evaluation_priority": "permit" }
]

Responses#

Status

Body

Meaning

200 OK

Resource types replaced.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

422 Unprocessable Entity

error message (string)

JSON body failed to deserialize or an item had an invalid evaluation_priority.

500 Internal Server Error

error message (string)

Storage failure while writing metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

GET /v1beta/services/{service}/resource-types/{type}/ — fetch resource type metadata#

Returns the ResourceTypeMeta for a single resource type.

Path parameters#

Name

Type

Description

service

string

Service name.

type

string

Resource type name.

Responses#

Status

Body

Meaning

200 OK

ResourceTypeMeta

Resource type found.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

404 Not Found

error message (string)

No resource type with that name is registered for this service.

500 Internal Server Error

error message (string)

Storage failure while reading metadata.

501 Not Implemented

error message (string)

Storage backend does not support this operation.

PUT /v1beta/services/{service}/resource-types/{type}/ — update resource type metadata#

Creates the resource type record if it does not exist, or updates it in place. The service and type fields of the payload are overwritten with the URL path segments, so the body effectively only needs to carry evaluation_priority.

Path parameters#

Name

Type

Description

service

string

Service name.

type

string

Resource type name.

Request#

PUT /v1beta/services/storage-service/resource-types/object/ HTTP/1.1
Host: permission-service.storage-apis.svc.cluster.local:3000
Authorization: Bearer <token>
Content-Type: application/json

{
  "evaluation_priority": "forbid"
}

Responses#

Status

Body

Meaning

200 OK

ResourceTypeMeta

Metadata stored; the response echoes the record as persisted.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

422 Unprocessable Entity

error message (string)

JSON body failed to deserialize or evaluation_priority was invalid.

500 Internal Server Error

error message (string)

Storage failure while writing metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

DELETE /v1beta/services/{service}/resource-types/{type}/ — delete resource type metadata#

Removes a single resource type from the service.

Path parameters#

Name

Type

Description

service

string

Service name.

type

string

Resource type name.

Responses#

Status

Body

Meaning

204 No Content

Resource type removed, or no such resource type existed.

401 Unauthorized

Missing or invalid bearer token (auth enabled).

403 Forbidden

error message (string)

Caller lacks permissions:meta.

500 Internal Server Error

error message (string)

Storage failure while deleting metadata.

501 Not Implemented

error message (string)

Storage backend does not support writes.

Evaluation priority for resources#

Each registered resource type carries an evaluation priority that controls how competing Cedar decisions are combined for requests targeting a resource of that type:

  • forbid (default) — if any matching policy evaluates to forbid, the request is denied. This is Cedar’s standard “deny overrides” behavior and is the safer default for resources where an explicit deny should always win.

  • permit — if any matching policy evaluates to permit, the request is allowed, even when another policy evaluates to forbid. This is useful for resource types where permits are intended to be additive exceptions.

Evaluation priority only affects resource-scoped requests; requests with no resource follow the service-wide default. For a complete description of how priorities interact with policy scopes and Cedar decisions, see How authorization requests are evaluated.

idClaim for principals#

The id_claim field on a ServiceMeta record selects which claim from the caller’s bearer token is used as the principal id when the caller targets that service. When unset, the service falls back to the deployment-wide PRINCIPAL_ID_CLAIM (default sub). Different services in the same deployment can therefore identify the same user by different ids — for example, email for one service and sub for another.

For the full resolution order (per-service idClaim, deployment-wide PRINCIPAL_ID_CLAIM, sub fallback) and for the principal attributes this affects, see Principal in the Cedar policy guide.

Interactive API reference#

When the service runs with the REST API enabled, the full OpenAPI definition is served at /openapi.json and an interactive Swagger UI is available at /swagger-ui on the REST port (default 3000). Those surfaces stay in sync with the service implementation and cover every field and schema referenced on this page.

See also#