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
operationsfield — set it to[schedule]. The API also acceptsterminate,raise,pause,resume,purge,get, andrerunfor forward compatibility, but these operations don't route cross-app yet, so policies have no effect on them. Activity rules have nooperationsfield 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
- Explore workflow patterns for chaining, fan-out/fan-in, and external events
- Learn how Catalyst secures the platform end to end
- See policies for the other declarative policy types you can bind to App IDs