Migrating from legacy workspaces

This guide covers migrating from the legacy Cerbos Hub workspace architecture to the current system based on policy stores and deployments. It applies to both service PDPs and embedded PDPs.

If you would prefer a guided migration, contact support@cerbos.dev. We are happy to walk you through the process.

What the new architecture provides

The legacy system coupled a single Git repository to a single workspace, with deployment labels defined in .cerbos-hub.yaml and a workspace secret for bundle encryption. The current architecture removes these constraints.

Multi-store composition

Deployments can combine policies from multiple independent policy stores. Teams, services, or tenants can own their policies separately while Cerbos Hub merges them at build time. The legacy system was limited to a single repository per workspace.

Source-agnostic ingestion

Policy stores accept input from Git repositories, CI/CD pipelines, the Cerbos Hub API, SDKs, the cerbosctl CLI, or browser upload. The legacy system required a GitHub repository. See supported ingestion methods.

No workspace secret

Bundle encryption is managed entirely by Cerbos Hub. There is no secret to generate, distribute, rotate, or lose. PDP configuration requires only a client ID, client secret, and deployment ID. See deploying a PDP.

Deployment-scoped credentials

Credentials are scoped to individual deployments rather than an entire workspace. This provides a narrower blast radius when credentials are rotated or revoked. See obtaining credentials.

Embedded PDP rules

Each deployment can define multiple ePDP rules with independent policy filtering (by resource, action, scope, role, and version), authentication requirements, and IP allowlists. The legacy system included all annotated policies in a single bundle with no access controls.

Automatic build pipeline

Every change to a policy store triggers compilation, test execution, and bundle distribution. The legacy system required .cerbos-hub.yaml label mappings to control which Git references produced builds. See build life cycle.

Migration overview

Migration involves five phases:

  1. Create a new workspace in Cerbos Hub

  2. Create a policy store and connect it to your Git repository

  3. Create a deployment using that policy store

  4. Update service PDP configuration to use the new credentials and deployment ID

  5. Update embedded PDP integration (if applicable)

Each phase can be completed independently. Legacy and current PDPs can run in parallel during the transition.

Architecture changes

Aspect Legacy Current

Policy source

Workspace (single Git repository, single policy source)

One or more policy stores per deployment

Bundle versioning

Deployment labels in .cerbos-hub.yaml (branch, tag, or commit hash)

Deployment ID; each policy store change triggers a new build automatically

Bundle encryption

Workspace secret (asymmetric key pair)

Managed by Cerbos Hub; no workspace secret required

Credentials

Workspace-scoped client ID, client secret, and workspace secret

Deployment-scoped client ID and client secret

Embedded PDP bundle addressing

URL with workspace and label query parameters

Opaque ePDP rule ID

Embedded PDP policy filtering

Per-policy YAML annotation (hub.cerbos.cloud/embedded-pdp)

Per-rule configuration in Hub UI (resources, actions, scopes, roles, versions)

Audit log collection

Same credentials as bundle distribution

Same credentials as bundle distribution (no workspace secret)

Phase 1: Create a new workspace

Policy stores and deployments are only available in new workspaces. Your legacy workspace cannot be upgraded in place, so you need to create a new one.

  1. Sign in to Cerbos Hub

  2. Create a new workspace

Phase 2: Create a policy store

A policy store replaces the workspace as the container for your policies. If your legacy workspace was connected to a GitHub repository, the policy store connects to the same repository.

  1. In the new workspace, navigate to Policy stores and click New store

  2. Enter a name for the store (e.g. the same name as your legacy workspace)

  3. Select GitHub repository as the source and authorize access if prompted

  4. Select the same repository your legacy workspace used

  5. Configure the branch to track

If your legacy .cerbos-hub.yaml defined a label pointing to a specific branch (e.g. main), use that branch as the policy store’s tracked branch.

Legacy label mapping
# .cerbos-hub.yaml (legacy)
apiVersion: api.cerbos.cloud/v1
labels:
  latest:
    branch: main

In this example, create a policy store tracking the main branch. The label abstraction is no longer needed — the policy store tracks the branch directly.

If your legacy setup used multiple labels pointing to different branches (e.g. production on main and staging on develop), create a separate policy store for each branch, then reference each in the appropriate deployment.

See Policy stores and GitHub integration for details.

Subdirectory configuration

If your policies are in a subdirectory of the repository, specify the path when configuring the GitHub connection. The policy store syncs only that directory and its children.

Non-GitHub sources

If your policies are not in GitHub, other ingestion methods are available: CI/CD pipelines, Cerbos Hub SDKs, the cerbosctl CLI, or browser upload. See Supported ingestion methods.

Phase 3: Create a deployment

A deployment replaces the combination of workspace + deployment label. It packages policies from one or more policy stores into versioned bundles and distributes them to connected PDPs.

  1. Navigate to Deployments and click New deployment

  2. Select the policy store created in Phase 2

  3. Click Create

Cerbos Hub runs an initial build. When it completes, note the deployment ID from the deployment detail page.

Generate client credentials

  1. On the deployment page, select the Client credentials tab

  2. Click Generate a client credential

  3. Choose Read & Write if you need audit log collection, or Read only for bundle distribution only

  4. Save the client ID and client secret — the secret is shown only once

These credentials replace the legacy workspace-scoped credentials and workspace secret.

Phase 4: Update service PDP configuration

The PDP configuration changes in two ways:

  • CERBOS_HUB_BUNDLE (label name) is replaced by CERBOS_HUB_DEPLOYMENT_ID

  • CERBOS_HUB_WORKSPACE_SECRET is no longer required

Legacy configuration

docker run --rm --name cerbos \
  -p 3592:3592 -p 3593:3593 \
  -e CERBOS_HUB_BUNDLE="latest" \
  -e CERBOS_HUB_WORKSPACE_SECRET="..." \
  -e CERBOS_HUB_CLIENT_ID="..." \
  -e CERBOS_HUB_CLIENT_SECRET="..." \
  ghcr.io/cerbos/cerbos:latest server

Updated configuration

docker run --rm --name cerbos \
  -p 3592:3592 -p 3593:3593 \
  -e CERBOS_HUB_DEPLOYMENT_ID="..." \
  -e CERBOS_HUB_CLIENT_ID="..." \
  -e CERBOS_HUB_CLIENT_SECRET="..." \
  ghcr.io/cerbos/cerbos:latest server

Use the deployment ID and credentials from Phase 3.

YAML configuration file

Legacy
hub:
  credentials:
    clientID: "..."
    clientSecret: "..."
    workspaceSecret: "..."

storage:
  driver: hub
  hub:
    remote:
      bundleLabel: latest
Updated
hub:
  credentials:
    clientID: "..."
    clientSecret: "..."

storage:
  driver: hub
  hub:
    remote:
      deploymentID: "..."

The workspaceSecret and bundleLabel fields are removed. The deploymentID field replaces bundleLabel.

Audit log collection

If audit log collection was enabled, the audit configuration block is unchanged:

audit:
  backend: hub
  hub:
    storagePath: "/var/cerbos/audit-buffer"

The audit backend uses the same deployment-scoped credentials configured in the hub.credentials block. No additional credential changes are needed.

Existing audit log data stored under your legacy workspace is not automatically migrated to the new deployment. Contact the Cerbos team at support@cerbos.dev to arrange migration of your historical audit logs.

Cerbos version requirement

The current deployment system requires Cerbos version 0.51.0 or later. If your PDPs are running an older version, upgrade before switching to the new credentials.

Rolling migration

Legacy and current PDPs can run in parallel. To migrate a fleet incrementally:

  1. Deploy updated configuration to a subset of instances

  2. Verify those instances connect to Cerbos Hub and receive the correct bundle (check the Decision points tab on the deployment page)

  3. Roll out to the remaining instances

Phase 5: Update embedded PDP integration

This phase applies only if you use the embedded PDP (WebAssembly-based authorization in browsers, edge functions, or other JavaScript environments). If you only use service PDPs, skip to Cleanup.

The embedded PDP requires new SDK packages and a different bundle addressing model.

Create an ePDP rule

Before updating client code, create an ePDP rule on the deployment:

  1. Navigate to the deployment in Cerbos Hub

  2. Select the Embedded PDP rules tab

  3. Click Create rule

  4. Configure policy filtering (see Policy filtering below)

  5. Configure access controls (see Access controls below)

  6. Click Save rule

  7. Copy the rule ID from the rule card

Policy filtering

The legacy system used a YAML annotation on individual policies to control inclusion in the bundle:

apiVersion: api.cerbos.dev/v1
metadata:
  annotations:
    hub.cerbos.cloud/embedded-pdp: "true"
resourcePolicy:
  version: default
  resource: purchase_order

The current system filters policies through the ePDP rule configuration in Cerbos Hub. The annotation is no longer used.

To replicate your existing filtering:

  1. Identify which policies had the hub.cerbos.cloud/embedded-pdp: "true" annotation

  2. Note the resource types, actions, roles, and scopes those policies cover

  3. Configure equivalent filters on the ePDP rule using the Resources and actions, Scopes, Roles, and Versions settings

If no policies in your legacy setup were annotated (all policies were included), leave all filters set to All on the new rule.

The hub.cerbos.cloud/embedded-pdp annotation can be removed from your policy files after migration is complete. It has no effect in the current system.

See Filtering policies for details on each filter type.

Access controls

The legacy embedded PDP did not support authentication or IP restrictions on bundle downloads. The current system supports both.

Authentication

If the bundle is served to end-user browsers and the authorization logic is not sensitive, Public access is appropriate. If the bundle is fetched server-side (Node.js, edge workers), use Client credential and store credentials in environment variables or a secrets manager.

IP allowlist

If bundle access should be restricted to specific network ranges, configure CIDR entries on the rule.

See Security controls for details.

Update SDK packages

Remove the legacy package and install the replacements:

npm uninstall @cerbos/embedded
npm install @cerbos/embedded-client @cerbos/embedded-server

If the rule uses client credential authentication, also install the @cerbos/hub package:

npm install @cerbos/hub

Update client initialization

Legacy
import { AutoUpdatingLoader, Embedded } from "@cerbos/embedded";

const cerbos = new Embedded(
  new AutoUpdatingLoader(
    "https://lite.cerbos.cloud/bundle?workspace=...&label=...",
  ),
);
Updated
import { Embedded } from "@cerbos/embedded-client";
import wasm from "@cerbos/embedded-server/server.wasm?init"; (1)

const cerbos = new Embedded({
  policies: { ruleId: "<RULE_ID>" },
  wasm,
});
1 The ?init import suffix is Vite-specific. See WebAssembly module loading below for other environments.

Replace <RULE_ID> with the rule ID from Cerbos Hub.

The AutoUpdatingLoader and the bundle URL are no longer used. The SDK fetches bundles and polls for updates based on the rule ID.

WebAssembly module loading

The legacy SDK bundled the WebAssembly runtime internally. The current SDK requires explicit loading of the WASM module. The mechanism depends on your build tooling:

Environment Import

Vite

import wasm from "@cerbos/embedded-server/server.wasm?init";

Webpack

import wasmUrl from "@cerbos/embedded-server/server.wasm";

Rspack

import wasmUrl from "@cerbos/embedded-server/server.wasm";

Next.js (Turbopack)

Copy WASM to public/ and pass the URL string (see WebAssembly module loading below)

Node.js

import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";

const wasm = readFile(
  fileURLToPath(import.meta.resolve("@cerbos/embedded-server/server.wasm")),
);

See Loading the WebAssembly module for all options.

Authenticated access

If the rule requires client credentials:

import { Embedded } from "@cerbos/embedded-client";
import { credentialsFromEnv } from "@cerbos/hub";
import wasm from "@cerbos/embedded-server/server.wasm?init";

const cerbos = new Embedded({
  policies: {
    ruleId: "<RULE_ID>",
    credentials: credentialsFromEnv(),
  },
  wasm,
});

This reads CERBOS_HUB_CLIENT_ID and CERBOS_HUB_CLIENT_SECRET from process.env.

Authorization check API

The authorization check methods (isAllowed, checkResource, checkResources) are unchanged between the legacy and current SDKs. Existing check calls do not require modification. Run your test suite to confirm authorization decisions match the legacy behavior.

The current SDK also supports planResources, which was not available in the legacy embedded PDP.

New capabilities

The current ePDP SDK adds several capabilities not present in the legacy version:

Dynamic scopes

Clients specify scopes at fetch time, so each tenant gets only its applicable policies without a dedicated rule. See Dynamic scopes.

Deferred activation

Download bundle updates without applying them immediately. See Deferred activation.

Update notifications

Register callbacks for bundle update success or failure. See Update notifications.

Decision callbacks

Capture authorization decisions locally via onDecision. See Configuration options.

Query planning

Use planResources to generate query plans. See planResources.

Multiple rules per deployment

Serve different bundle configurations to different clients. See ePDP rules.

Cleanup

After confirming all PDPs are running on the new configuration:

  1. Remove the .cerbos-hub.yaml deployment labels file from your repository (this was only used by the legacy workspace system)

  2. Remove the hub.cerbos.cloud/embedded-pdp annotations from policy files, if present

  3. Verify that the @cerbos/embedded package is no longer referenced in your codebase (embedded PDP only)

  4. Revoke the legacy workspace client credentials

  5. Delete or archive the legacy workspace