This cookbook provides comprehensive real-world scenarios for using Housecarl's policy engine to solve authorization challenges. Each scenario demonstrates how to structure domains, policies, and resources to achieve different access control patterns with complete setup workflows.
For quick policy patterns and recipes, see the Policy Recipes. For a formal understanding of the algorithm, see the Algorithm Reference. For the policy language details, see the Language Reference.
Before diving into scenarios, understand these key concepts:
hc://domain/<domain-uuid>/path/to/resourceFixed: Exact string matching (fastest, use for exact matches)Prefix: String prefix matching (ideal for hierarchical resources)RegEx: Regular expression matching (most flexible, higher computational cost)All scenarios in this cookbook can be tested offline using housectl authz can-i-local. This command evaluates authorization requests against local policy files without requiring a running housecarl server.
Server-backed example note: The UUIDs in this cookbook are illustrative. If you run the
housectl authz can-iexamples against a live server, replace those UUIDs with the real domain UUIDs from your environment first, or the server returnsdomain not found.
Basic Usage:
# Test a request against local policies
housectl authz can-i-local \
--request request.json \
--tenant <tenant-uuid> \
./policies/
# With domain specified
housectl authz can-i-local \
--request request.json \
--tenant <tenant-uuid> \
--domain <domain-name-or-uuid> \
./policies/
Policy File Format: Policies are written in TOML format (see examples below)
Request File Format: JSON file with a context object containing subject, action, object
A standard CRUD (Create, Read, Update, Delete) application where users can manage their own resources, and administrators have full access.
Tenant ID: 550e8400-e29b-41d4-a716-446655440000
Tenant Name: myapp-tenant
Domain: app-domain
Resources: hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/users/{user_id}
hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/posts/{post_id}
hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/comments/{comment_id}
# Create tenant (returns tenant UUID)
housectl admin create "myapp-tenant" "My CRUD Application"
# Example output: Tenant created with ID: 550e8400-e29b-41d4-a716-446655440000
# Login as admin
housectl config login http://localhost:1234 admin --tenant myapp-tenant
# Create domain
housectl domain create app-domain
# Example output: Domain created with ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7
Policies are written in TOML format. Create a directory for your policies and save each policy as a separate .toml file.
Policy 1: Admin Full Access (admin-full-access.toml)
Administrators can perform any action on any resource.
name = "admin-full-access"
description = "Administrators have full access to all resources"
invert = false
deny = false
engine = "RegEx"
statements = [
{ "subject" = "admin", "action" = ".*", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/.*" }
]
Policy 2: User Self-Management (user-self-management.toml)
Users can read and update their own user records.
name = "user-self-management"
description = "Users can manage their own user records"
invert = false
deny = false
engine = "Fixed"
statements = [
{ "subject" = "$current_user()", "action" = "read", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/users/$current_user()" },
{ "subject" = "$current_user()", "action" = "update", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/users/$current_user()" }
]
Policy 3: Post Ownership (post-ownership.toml)
Users can create posts and manage (update/delete) their own posts. Anyone can read posts.
name = "post-ownership"
description = "Users manage their own posts, everyone can read"
invert = false
deny = false
engine = "RegEx"
statements = [
{ "action" = "read", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/posts/.*" },
{ "subject" = "$current_user()", "owner" = "$current_user()", "action" = "(update|delete)", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/posts/.*" },
{ "subject" = "$current_user()", "action" = "create", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/posts" }
]
Policy 4: Comment Management (comment-management.toml)
Users can create comments and delete their own comments.
name = "comment-management"
description = "Users can create and delete their own comments"
invert = false
deny = false
engine = "Fixed"
statements = [
{ "action" = "read", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/comments" },
{ "subject" = "$current_user()", "action" = "create", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/comments" },
{ "subject" = "$current_user()", "owner" = "$current_user()", "action" = "delete", "object" = "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/comments" }
]
If deploying to a running housecarl server, save the policies to a directory and upload them:
# Create policies directory
mkdir -p ./scenario1-policies
# Save each policy from above to ./scenario1-policies/*.toml
# Upload policies to server
housectl domain put-policies <app-domain-uuid> ./scenario1-policies/
Test your policies offline using housectl authz can-i-local. Create JSON request files for each test case:
Test Case 1: Admin deletes post (admin-delete-post.json)
{
"context": {
"subject": "admin",
"action": "delete",
"object": "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/posts/123"
}
}
Test Case 2: Alice reads own profile (alice-read-own-profile.json)
{
"context": {
"subject": "alice",
"action": "read",
"object": "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/users/alice"
}
}
Test Case 3: Alice tries to update Bob's profile (alice-update-bob.json)
{
"context": {
"subject": "alice",
"action": "update",
"object": "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/users/bob"
}
}
Test Case 4: Alice deletes own post (alice-delete-own-post.json)
{
"context": {
"subject": "alice",
"owner": "alice",
"action": "delete",
"object": "hc://domain/550e8400-e29b-41d4-a716-446655440000/app-domain/myapp/posts/456"
}
}
Run the tests:
# Test admin access (should succeed - returns ALLOW)
housectl authz can-i-local \
--request admin-delete-post.json \
--tenant 550e8400-e29b-41d4-a716-446655440000 \
./scenario1-policies/
# Test user reading own profile (should succeed)
housectl authz can-i-local \
--request alice-read-own-profile.json \
--tenant 550e8400-e29b-41d4-a716-446655440000 \
./scenario1-policies/
# Test user updating another user (should fail - returns DENY)
housectl authz can-i-local \
--request alice-update-bob.json \
--tenant 550e8400-e29b-41d4-a716-446655440000 \
./scenario1-policies/
# Test user deleting own post (should succeed)
housectl authz can-i-local \
--request alice-delete-own-post.json \
--tenant 550e8400-e29b-41d4-a716-446655440000 \
./scenario1-policies/
Expected Results:
A CRUD application where a dedicated team manages secrets and configuration, while the application has read-only access to those secrets. This models a separation of concerns where operators manage sensitive data and applications consume it.
Note: This scenario follows the same pattern as Scenario 1. All policies should be written in TOML format and can be tested locally with housectl authz can-i-local.
Tenant ID: 660e8400-e29b-41d4-a716-446655440001
Tenant Name: platform-tenant
Domains:
- secrets-domain (superior)
- app-domain (inherits from secrets-domain)
Resources:
- hc://domain/660e8400-e29b-41d4-a716-446655440001/secrets-domain/platform/secrets/{secret_name} (managed by ops-team)
- hc://domain/660e8400-e29b-41d4-a716-446655440001/secrets-domain/platform/config/{config_name} (managed by ops-team)
- hc://domain/660e8400-e29b-41d4-a716-446655440001/app-domain/platform/app/data/{resource_id} (managed by app)
# Create tenant
housectl admin create "platform-tenant" "Platform with shared resources"
# Login
housectl config login http://localhost:1234 admin --tenant platform-tenant
# Create secrets domain (superior)
housectl domain create secrets-domain platform-tenant
# Create app domain (inherits from secrets-domain)
housectl domain create app-domain platform-tenant --superior-domains secrets-domain
Policy 1: Ops Team Full Access to Secrets The operations team can create, read, update, and delete secrets and configuration.
name = "ops-team-secrets-management"
description = "Ops team has full access to secrets and config"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "ops-team"
action = "(create|read|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/.*"
[[statements]]
subject = "ops-team"
action = "(create|read|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/config/.*"
Policy 2: Application Read-Only Access to Secrets The application service accounts can only read secrets and configuration.
name = "app-readonly-secrets"
description = "Applications have read-only access to secrets"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "svc:crud-app"
action = "read"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/.*"
[[statements]]
subject = "svc:crud-app"
action = "read"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/config/.*"
Policy 3: Deny Application Write to Secrets Explicitly prevent applications from modifying secrets (defense in depth).
name = "deny-app-write-secrets"
description = "Prevent applications from writing secrets"
invert = false
deny = true
engine = "RegEx"
[[statements]]
subject = "svc:.*"
action = "(create|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/.*"
Policy 4: Application Data Management The application can fully manage its own data resources.
name = "app-data-management"
description = "Application manages its own data"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "svc:crud-app"
action = "(create|read|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/app/data/.*"
Policy 5: User Read Access to App Data End users can read application data based on ownership.
name = "user-read-app-data"
description = "Users can read their own app data"
invert = false
deny = false
engine = "Fixed"
[[statements]]
subject = "$current_user()"
owner = "$current_user()"
action = "read"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/app/data"
# Apply secrets domain policies
mkdir -p secrets-policies app-policies
cat > secrets-policies/ops-team-secrets-management.toml <<'EOF'
name = "ops-team-secrets-management"
description = "Ops team has full access to secrets and config"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "ops-team"
action = "(create|read|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/.*"
[[statements]]
subject = "ops-team"
action = "(create|read|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/config/.*"
EOF
cat > secrets-policies/app-readonly-secrets.toml <<'EOF'
name = "app-readonly-secrets"
description = "Applications have read-only access to secrets"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "svc:crud-app"
action = "read"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/.*"
[[statements]]
subject = "svc:crud-app"
action = "read"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/config/.*"
EOF
cat > secrets-policies/deny-app-write-secrets.toml <<'EOF'
name = "deny-app-write-secrets"
description = "Prevent applications from writing secrets"
invert = false
deny = true
engine = "RegEx"
[[statements]]
subject = "svc:.*"
action = "(create|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/.*"
EOF
housectl domain put-policies <secrets-domain-uuid> secrets-policies/
# Apply app domain policies
cat > app-policies/app-data-management.toml <<'EOF'
name = "app-data-management"
description = "Application manages its own data"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "svc:crud-app"
action = "(create|read|update|delete)"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/app/data/.*"
EOF
cat > app-policies/user-read-app-data.toml <<'EOF'
name = "user-read-app-data"
description = "Users can read their own app data"
invert = false
deny = false
engine = "Fixed"
[[statements]]
subject = "$current_user()"
owner = "$current_user()"
action = "read"
object = "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/app/data"
EOF
housectl domain put-policies <app-domain-uuid> app-policies/
Replace the sample UUIDs in these request files with the real domain UUIDs you created before running the live housectl authz can-i commands below.
# Ops team creates a secret (should succeed)
cat > ops-create-secret.json <<'EOF'
{
"context": {
"subject": "ops-team",
"action": "create",
"object": "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/db-password"
}
}
EOF
housectl authz can-i ops-create-secret.json
# Application reads secret (should succeed - inherits from secrets-domain)
cat > app-read-secret.json <<'EOF'
{
"context": {
"subject": "svc:crud-app",
"action": "read",
"object": "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/db-password"
}
}
EOF
housectl authz can-i app-read-secret.json
# Application tries to update secret (should fail - deny policy)
cat > app-update-secret.json <<'EOF'
{
"context": {
"subject": "svc:crud-app",
"action": "update",
"object": "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/secrets/db-password"
}
}
EOF
housectl authz can-i app-update-secret.json
# Application manages its data (should succeed)
cat > app-create-data.json <<'EOF'
{
"context": {
"subject": "svc:crud-app",
"action": "create",
"object": "hc://domain/660e8400-e29b-41d4-a716-446655440001/platform/app/data/record-123"
}
}
EOF
housectl authz can-i app-create-data.json
Three teams (Operations, Development, Compliance) need different levels of access to review and approve a CRUD application, its data pipeline, and deployment configuration. This models a GitOps-style workflow with separation of duties.
Note: This scenario follows the same pattern as Scenario 1. All policies should be written in TOML format and can be tested locally with housectl authz can-i-local.
Tenant ID: 770e8400-e29b-41d4-a716-446655440002
Tenant Name: enterprise-tenant
Domain Hierarchy:
- compliance-domain (root)
- ops-domain (inherits compliance)
- dev-domain (inherits ops)
Resources:
- hc://domain/770e8400-e29b-41d4-a716-446655440002/compliance-domain/enterprise/app/code/* (source code)
- hc://domain/770e8400-e29b-41d4-a716-446655440002/compliance-domain/enterprise/app/config/* (application config)
- hc://domain/770e8400-e29b-41d4-a716-446655440002/compliance-domain/enterprise/pipeline/* (data pipeline definitions)
- hc://domain/770e8400-e29b-41d4-a716-446655440002/compliance-domain/enterprise/deployment/* (K8s manifests, Terraform)
- hc://domain/770e8400-e29b-41d4-a716-446655440002/compliance-domain/enterprise/compliance/* (audit reports, policies)
# Create tenant
housectl admin create "enterprise-tenant" "Enterprise multi-team environment"
# Login
housectl config login http://localhost:1234 admin --tenant enterprise-tenant
# Create domain hierarchy
housectl domain create compliance-domain enterprise-tenant
housectl domain create ops-domain enterprise-tenant --superior-domains compliance-domain
housectl domain create dev-domain enterprise-tenant --superior-domains ops-domain
Policy 1: Compliance Team Read-All Access Compliance can read everything but cannot modify operational systems.
name = "compliance-read-all"
description = "Compliance team has read access to all resources"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "compliance-team"
action = "read"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/.*"
Policy 2: Compliance Team Manages Compliance Resources Compliance can manage compliance-specific resources (audit reports, policy documents).
name = "compliance-manage-compliance-resources"
description = "Compliance team manages compliance resources"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "compliance-team"
action = "(create|update|delete|approve)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/compliance/.*"
Policy 3: Deny Non-Compliance Modification of Compliance Resources Only compliance team can modify compliance resources.
name = "deny-non-compliance-write"
description = "Only compliance can modify compliance resources"
invert = false
deny = true
engine = "RegEx"
[[statements]]
subject = "(?!compliance-team).*"
action = "(create|update|delete)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/compliance/.*"
Policy 4: Ops Team Deployment Management Operations team can read and deploy infrastructure and application configurations.
name = "ops-deployment-management"
description = "Ops team manages deployments and infrastructure"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "ops-team"
action = "(read|deploy|rollback)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/.*"
[[statements]]
subject = "ops-team"
action = "(read|update)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/config/.*"
Policy 5: Ops Team Pipeline Management Operations can read and execute data pipelines.
name = "ops-pipeline-management"
description = "Ops team manages data pipelines"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "ops-team"
action = "(read|execute|monitor)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/pipeline/.*"
Policy 6: Ops Approval Required for Production Deployments Production deployments require ops team approval.
name = "ops-approve-prod-deployment"
description = "Ops must approve production deployments"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "ops-team"
action = "approve"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/.*"
Policy 7: Dev Team Source Code Management Developers can manage source code and create merge requests.
name = "dev-code-management"
description = "Developers manage source code"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "dev-team"
action = "(read|write|create|update)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/code/.*"
Policy 8: Dev Team Limited Config Access Developers can read configs and propose changes but cannot deploy directly.
name = "dev-config-read-propose"
description = "Developers can read and propose config changes"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "dev-team"
action = "read"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/config/.*"
[[statements]]
subject = "dev-team"
action = "propose"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/config/.*"
Policy 9: Dev Team Pipeline Read Access Developers can read pipeline definitions and logs for debugging.
name = "dev-pipeline-read"
description = "Developers can read pipeline definitions and logs"
invert = false
deny = false
engine = "RegEx"
[[statements]]
subject = "dev-team"
action = "(read|logs)"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/pipeline/.*"
Policy 10: Deny Dev Direct Production Deployment Developers cannot deploy directly to production.
name = "deny-dev-prod-deploy"
description = "Prevent developers from deploying to production"
invert = false
deny = true
engine = "RegEx"
[[statements]]
subject = "dev-team"
action = "deploy"
object = "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/.*"
# Save the policies above into per-domain directories (one policy per file)
mkdir -p compliance-policies ops-policies dev-policies
# Compliance policies: compliance-read-all.toml, compliance-manage-compliance-resources.toml,
# deny-non-compliance-write.toml
housectl domain put-policies <compliance-domain-uuid> compliance-policies/
# Ops policies: ops-deployment-management.toml, ops-pipeline-management.toml,
# ops-approve-prod-deployment.toml
housectl domain put-policies <ops-domain-uuid> ops-policies/
# Dev policies: dev-code-management.toml, dev-config-read-propose.toml,
# dev-pipeline-read.toml, deny-dev-prod-deploy.toml
housectl domain put-policies <dev-domain-uuid> dev-policies/
Replace the sample UUIDs in these request files with the real domain UUIDs from your deployed domains before running the live housectl authz can-i commands below.
# Compliance reads deployment config (should succeed)
cat > compliance-read-deploy.json <<'EOF'
{
"context": {
"subject": "compliance-team",
"action": "read",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/k8s-manifests"
}
}
EOF
housectl authz can-i compliance-read-deploy.json
# Compliance tries to deploy (should fail - no deploy policy)
cat > compliance-deploy.json <<'EOF'
{
"context": {
"subject": "compliance-team",
"action": "deploy",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/k8s-manifests"
}
}
EOF
housectl authz can-i compliance-deploy.json
# Ops deploys to production (should succeed - inherits compliance read + has deploy)
cat > ops-deploy.json <<'EOF'
{
"context": {
"subject": "ops-team",
"action": "deploy",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/k8s-manifests"
}
}
EOF
housectl authz can-i ops-deploy.json
# Ops reads source code (should succeed - inherits from compliance)
cat > ops-read-code.json <<'EOF'
{
"context": {
"subject": "ops-team",
"action": "read",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/code/main.rs"
}
}
EOF
housectl authz can-i ops-read-code.json
# Developer writes code (should succeed)
cat > dev-write-code.json <<'EOF'
{
"context": {
"subject": "dev-team",
"action": "write",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/code/main.rs"
}
}
EOF
housectl authz can-i dev-write-code.json
# Developer tries to deploy to production (should fail - deny policy)
cat > dev-deploy-prod.json <<'EOF'
{
"context": {
"subject": "dev-team",
"action": "deploy",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/k8s-manifests"
}
}
EOF
housectl authz can-i dev-deploy-prod.json
# Developer proposes config change (should succeed)
cat > dev-propose-config.json <<'EOF'
{
"context": {
"subject": "dev-team",
"action": "propose",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/config/database.toml"
}
}
EOF
housectl authz can-i dev-propose-config.json
# Developer reads pipeline logs (should succeed - inherits compliance read + has logs action)
cat > dev-pipeline-logs.json <<'EOF'
{
"context": {
"subject": "dev-team",
"action": "logs",
"object": "hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/pipeline/etl-job"
}
}
EOF
housectl authz can-i dev-pipeline-logs.json
A typical workflow in this setup:
hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/code/feature.rshc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/config/feature-flags.tomlhc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/app-v2deploy action on production resourceslogs action on pipeline resourcesStructure your domains from least-privileged (root) to most-privileged (leaf) so that broader policies propagate down. This prevents accidental privilege escalation.
Use explicit deny policies with "deny": true for sensitive resources like production deployments or secrets. Deny policies take precedence over allow policies.
Use macros like $current_user(), $resource_owner(), and $current_time() to create flexible, context-aware policies that reduce duplication.
hc://domain/<uuid>/app/users/alice/)Choose the simplest engine that meets your requirements - Fixed and Prefix have lower computational overhead than RegEx.
Always test authorization policies with housectl authz can-i before deploying to production. Create test scenarios covering both allowed and denied cases.
Use descriptive policy names and detailed descriptions. Future operators need to understand why a policy exists.
Periodically review policies with compliance team to ensure they still match business requirements and security posture.