This episode covers advanced policy authoring in OpenClaw: creating reusable policy templates, parameterized rules with policy inheritance, and using conditionals and service labels in rules to build policies that are flexible and easy to maintain.

In episode 7 you could see everything: deny rate, traffic flows, and anomalies surfacing on the dashboard. Now imagine you have thirteen services, each needing a similar policy, and you write the same policy over and over with slightly different names. The result? Bloated YAML files, inconsistency, and headaches during review. This is the problem advanced policy authoring solves.
We enter Phase 3: workloads, configuration, and data management. This episode's roadmap has three items: writing reusable policy templates so policies can be shared; understanding parameterized rules and policy inheritance so one template serves many cases; and using conditionals and service labels in rules for dynamic logic. By the end of the episode you'll write policies that are much smaller but far more powerful.
Every team usually needs similar policy patterns: default deny, allow access from frontend to a specific backend, block admin paths for regular users. If everyone copy-pastes YAML blocks, drift eventually appears — a policy is changed in one place but not another, and security behavior becomes inconsistent. Templates solve this with a single source of truth.
A template in OpenClaw is a policy skeleton that defines the structure and basic rules, but leaves certain parts empty for users to fill in. You define the template once, and policy instances just reference it and fill in the parameters.
apiVersion: openclaw.io/v1
kind: PolicyTemplate
metadata:
name: backend-access
namespace: openclaw-templates
spec:
parameterSchema:
- name: namespace
required: true
- name: service
required: true
- name: allowedClients
type: array
default: ["frontend-sa"]
template: |
apiVersion: openclaw.io/v1
kind: ServicePolicy
metadata:
name: {{.service}}-access
namespace: {{.namespace}}
spec:
selector:
labels:
app: {{.service}}
rules:
- from:
- serviceAccount: {{.allowedClients}}
action: ALLOW
- from:
- any: true
action: DENYNotice the parameterSchema section: it declares which parameters are required and optional, along with their defaults. The template section contains the actual policy skeleton with parameter name placeholders. This way, the default deny rule is automatically applied to every instance without being rewritten. The command openclawctl policy validate conditional-access --namespace payments is an example of validating a policy that uses labels from the selector.
Using a template is much more compact than writing a policy from scratch. You just name the template being used and fill in the parameters. OpenClaw will render the policy instance from the template when it's applied.
apiVersion: openclaw.io/v1
kind: ServicePolicy
metadata:
name: billing-access
namespace: billing
spec:
templateRef:
name: backend-access
namespace: openclaw-templates
parameters:
service: billing
allowedClients: ["order-sa", "inventory-sa"]Info
Store templates in a separate namespace dedicated to templates, with strict access control. Templates are executable code — anyone who can change a template indirectly changes every policy that uses it. Treat templates as having the same access rights as a cluster admin.
Parameterized rules mean the logic inside a rule is no longer hardcoded in YAML — it takes values from parameters. These values can come from the policy instance, or be inherited from the parent template. The benefit is clear: you change the behavior of many policies by changing one parameter value, not by editing them one by one.
Inheritance works from template toward instance. The template defines default values, and the instance may override some or all parameters. This is the same pattern as variable inheritance in programming languages — defaults in the parent, overrides in the child.
apiVersion: openclaw.io/v1
kind: PolicyTemplate
metadata:
name: tiered-access
spec:
parameterSchema:
- name: method
default: GET
- name: rateLimit
default: "100rps"
- name: enforceMfa
type: bool
default: true
template: |
apiVersion: openclaw.io/v1
kind: ServicePolicy
spec:
rules:
- methods: ["{{.method}}"]
rateLimit: {{.rateLimit}}
mfaRequired: {{.enforceMfa}}An instance using this template can let method use the default GET, or override it to POST for specific cases. The enforceMfa value can be turned off for internal services but enabled for public APIs — all without changing the template.
When inheritance is nested, you need to know the priority order: the most specific value wins. A policy instance beats a first-level template, and a first-level template beats a global default template. This principle also applies to rules within a single policy — more specific rules are evaluated before more general ones, and a DENY rule always wins over ALLOW when both match.
1. Policy instance parameters (most specific)
2. Child template parameters
3. Parent / default template parameters
4. DENY rules take precedence over ALLOW
5. Specific rules are evaluated before general rulesThis order answers the classic question "why is my request denied when there's an ALLOW rule?" — the answer is almost always: there's a more specific or higher-priority DENY rule that matched first.
Sometimes permissions depend on conditions that a static list can't represent. OpenClaw provides conditional expressions in rules — comparisons, AND and OR logic, and label existence checks. This enables adaptive rules: for example, the /internal path is only accessible during working hours, or services in the staging namespace may talk freely while production is restricted.
apiVersion: openclaw.io/v1
kind: ServicePolicy
metadata:
name: conditional-access
namespace: payments
spec:
selector:
labels:
app: settlement
rules:
- condition:
anyOf:
- { source.namespace: "frontend", time.hour: { gte: 8, lte: 18 } }
- { source.labels.env: "internal-tooling" }
action: ALLOW
- from:
- any: true
action: DENYNote the expression above: access is allowed if the source is from the frontend namespace and the time is between 8 and 18, OR if the source carries the label env: internal-tooling. This isn't just a static allow list — it's a decision evaluated per request based on the context at that moment.
Service labels are metadata attached to workloads, and in OpenClaw labels are one of the main ingredients for matching rules. Instead of writing one policy per service, you can write one policy that matches a group of services based on a shared label. As a result, a new service just needs the right label and is automatically covered by existing policies.
kubectl label deployment settlement payments-tier=high-risk
openclawctl policy validate conditional-access --namespace paymentsThe first command adds the payments-tier: high-risk label to the settlement deployment. The second command validates the policy against the current label state. With this pattern, onboarding a new service no longer means writing a new policy — just give it the right label, and the label-based policy applies immediately.
In episode 8 you changed how you write policies: from copy-pasting YAML to building reusable templates, from hardcoded values to parameterized rules with inheritance, and from static lists to conditional rules that leverage service labels. Your policies are now shorter, more consistent, and easier to review.
Key takeaways:
In the next episode, episode 9, we'll bring your policies to life: dynamic policy updates. You'll apply policy changes without downtime, validate and roll back safely, and manage the policy lifecycle through GitOps. See you there!