Skip to main content

Secure workflows with access policies

In a multi-application workflow, a parent workflow running on one app schedules child workflows and activities on another app. By default, any app in a Catalyst project can do this to any other app. Workflow access policies let you lock this down: you declare which caller apps are allowed to schedule which workflows and activities on a target app, and Catalyst denies everything else.

A WorkflowAccessPolicy is a project-level resource that you manage with the diagrid workflow access-policy commands or apply declaratively as a YAML manifest.

How policies work

A policy has two parts:

  • Scopes — the target App IDs the policy protects. Leave scopes empty to protect every app in the project.
  • Rules — each rule grants a list of callers (App IDs) access to specific workflows and activities on the target apps. Names are matched exactly or as glob patterns: * matches any sequence of characters, ? matches a single character, and [abc] matches a character set.

Policies are a pure allow-list. As soon as a policy covers an app, every cross-app workflow call to that app must match a rule — if no rule matches, Catalyst denies the call with a PermissionDenied error. Without any policy in place, all calls are allowed.

Three behaviors to keep in mind:

  • Self-calls are always allowed. A policy only gates cross-app calls; an app can always schedule and manage its own workflows and activities, so it never needs to list itself as a caller.
  • Multiple policies combine. When several policies cover the same app, a call is allowed if any rule in any of them matches.
  • Scheduling is what's enforced today. Workflow rules require an operations field — set it to [schedule]. The API also accepts terminate, raise, pause, resume, purge, get, and rerun for forward compatibility, but these operations don't route cross-app yet, so policies have no effect on them. Activity rules have no operations field because activities only support scheduling.

Create a policy

The fastest way to create a policy is with the --scopes and --callers flags:

diagrid workflow access-policy create order-app-policy \
--project my-project \
--scopes order-app \
--callers orchestrator-app,reporting-app

This protects order-app and allows orchestrator-app and reporting-app to schedule every workflow and activity on it. Any other app in the project that tries is denied.

Fine-grained rules with a YAML manifest

The --callers flag is all-or-nothing. To grant access to specific workflows and activities, define the policy as a YAML manifest and apply it:

# order-app-policy.yaml
apiVersion: dapr.io/v1alpha1
kind: WorkflowAccessPolicy
metadata:
name: order-app-policy
scopes:
- order-app
spec:
rules:
- callers:
- appID: orchestrator-app
workflows:
- name: order-processing
operations: [schedule]
- name: "report-*"
operations: [schedule]
activities:
- name: charge-payment
diagrid workflow access-policy create -f order-app-policy.yaml --project my-project

Here orchestrator-app can schedule the order-processing workflow, any workflow whose name starts with report-, and the charge-payment activity on order-app — and nothing else. Prefer exact names over glob patterns where you can; a broad pattern like * grants more than you might intend.

Start from deny by default

For the strictest posture, apply a single policy with no scopes and no rules. It covers every app in the project, and because policies are a pure allow-list, it denies every cross-app workflow call:

# default-deny.yaml
apiVersion: dapr.io/v1alpha1
kind: WorkflowAccessPolicy
metadata:
name: default-deny
spec:
rules: []

Each app can still run its own workflows and activities, since self-calls are always allowed. From this baseline, layer on narrowly scoped policies like the earlier examples to grant only the access each caller needs.

Manage policies

List the policies in a project:

diagrid workflow access-policy list --project my-project

Inspect a policy, including its full rule set:

diagrid workflow access-policy get order-app-policy --project my-project -o yaml

Update a policy with flags. The flags replace the corresponding fields of the existing policy, so the example below replaces all existing rules with a single grant that allows orchestrator-app to schedule everything:

diagrid workflow access-policy update order-app-policy \
--project my-project \
--callers orchestrator-app

To change fine-grained rules, edit the YAML manifest and run diagrid workflow access-policy update -f — the manifest replaces the existing policy.

Delete a policy to lift its restrictions:

diagrid workflow access-policy delete order-app-policy --project my-project

Verify a policy

To confirm a policy is enforced, schedule a workflow or activity from an app that no rule allows. The caller receives a PermissionDenied error:

access denied by workflow access policy: app 'reporting-app' operation 'schedule' on workflow 'order-processing'

Calls that match a rule continue to succeed, and the target app never sees the denied requests.

Next steps