Database Configuration#
The Permission Service supports two storage backends: PostgreSQL (default) and Config File (read-only YAML files).
Selecting the Database Type#
Set the DATABASE_TYPE environment variable or --database-type CLI argument:
Value |
Description |
|---|---|
|
Uses PostgreSQL as the read-write storage backend (default). |
|
Uses YAML files as a read-only storage backend; writes are not supported. |
Helm Chart#
All storage-related settings are under the database section in Helm values:
database:
type: "config-file" # or "postgres"
# Initialization data for policies and metadata.
# The chart creates a ConfigMap and mounts these as files inside the container.
# In config-file mode, these files are used as the storage backend.
# In postgres mode, policies are loaded as system policies on startup.
init:
policies:
- policy: "permit(principal, action, resource);"
metadata:
services:
- name: "my-service"
# Postgres settings (ignored when type is "config-file")
postgres:
host: "postgres-host"
port: 5432
user: "postgres"
# ... other postgres settings
When database.init.policies or database.init.metadata are provided, the chart creates a ConfigMap and mounts it at
/etc/permission-config/. The INIT_POLICIES_FILE and INIT_METADATA_FILE env vars are set to the mounted file paths.
In config-file mode, these files are used as the read-only storage backend. The database.postgres settings are
ignored.
In postgres mode, policies from database.init.policies are loaded as system policies on startup.
Environment Variables#
Variable |
Description |
|---|---|
|
|
|
Path to the policy YAML file on disk |
|
Path to the metadata YAML file on disk |
Config File Mode#
In config file mode, the service reads policies and metadata from YAML files on disk. Both files are automatically reloaded when their modification time changes — no service restart is required.
All write operations (add_policy, update_policy, remove_policy, set_service_meta, etc.) return a “not supported”
error.
Policy File Format#
policies:
- policy: "<cedar_policy_text>"
Each entry under policies must include a policy field with Cedar policy text. Scope fields are inferred from Cedar
text for both database backends.
The same inference behavior is used by REST write APIs (PUT /v1beta/policies/ and
PUT /v1beta/policies/batch/): each payload item must contain exactly one Cedar statement. Client-provided
principal/action/resource fields are ignored when present.
Scope Inference#
Every policy stored in the service has three optional scopes: principal, action, and resource. When an authorization request arrives, the service fetches all policies whose scopes match the request — a policy is fetched when its scope equals the corresponding request field, or when its scope is not set. A policy with an unset scope is called a global policy for that scope: it is fetched regardless of the value provided in the authorization request.
After fetching, the Cedar policy text is evaluated against the full request to produce the final allow or deny decision.
Scopes control which policies are fetched; Cedar controls the outcome. For how fetched policies are grouped by order and combined under each resource type’s evaluation priority, see How authorization requests are evaluated.
When a policy is loaded from the configuration file, the service reads the Cedar text and infers each scope automatically:
If the Cedar constraint is an exact equality, the scope is set to that value. For example,
principal == Principal::"alice"sets the principal scope toalice, andaction == Action::"storage:read"sets the action scope tostorage:read.If the Cedar constraint cannot be reduced to a single exact value (for example
in,is, lists with multiple values, or an unconstrainedprincipal/action/resource), the scope is left unset and the policy becomes global for that scope.
A policy where all three scopes are unset — such as permit(principal, action, resource); — is fully global. It is
fetched for every authorization request, and Cedar alone decides whether to allow or deny.
Each policy entry must contain exactly one Cedar policy statement. If an entry contains multiple statements, policy loading fails.
Limitations#
Scopes are used for policy retrieval only. Cedar policy text is always authoritative for the final authorization decision.
Resource IDs inferred from Cedar follow the same normalization and encoding behavior used by policy validation.
Policy Example#
policies:
# Global permit — no constraints, applies to everything
- policy: "permit(principal, action, resource);"
# Scoped to a specific action
- policy: 'permit(principal, action == Action::"my-service:read", resource);'
# Scoped to action + principal + resource
- policy: >-
permit(
principal == User::"user123",
action == Action::"my-service:write",
resource == Resource::"my-service:document/doc1"
);
# Cedar policy with a "when" condition
- policy: >-
permit(principal, action, resource)
when { principal.sub == "admin" && resource.tag == "public" };
# Cedar policy with an "unless" condition — no constraints (global)
- policy: >-
forbid(principal, action, resource)
unless { principal.sub == "superadmin" };
Metadata File Format#
services:
- name: "<service_name>"
principal:
idClaim: "<claim>" # optional, JWT claim used as principal ID
actions: # optional, list of action names
- "<action_name>"
resourceTypes: # optional, list of resource types
- type: "<resource_type>"
evaluationPriority: "<permit|forbid>" # optional, defaults to "forbid"
Metadata Example#
services:
- name: "my-service"
principal:
idClaim: "sub"
actions:
- "read"
- "write"
resourceTypes:
- type: "document"
evaluationPriority: "forbid"