PRE-RELEASE INFORMATION: SUBJECT TO CHANGE

Policy Cookbook

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.

Core Concepts

Before diving into scenarios, understand these key concepts:

  • Resource: A hierarchical identifier in the format hc://domain/<domain-uuid>/path/to/resource
  • Policy: A set of rules (written in TOML format) that evaluate whether an action is allowed
  • Domain: A container for policies, can inherit from superior domains
  • Statement: Individual rules within a policy using key-value pairs
  • Evaluation Engines:
    • Fixed: 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)

Testing Policies Locally

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-i examples against a live server, replace those UUIDs with the real domain UUIDs from your environment first, or the server returns domain 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

Scenario 1: Typical CRUD Application

Overview

A standard CRUD (Create, Read, Update, Delete) application where users can manage their own resources, and administrators have full access.

Architecture

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}

Setup

1. Create the Tenant and Domain

# 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

2. Define Policies

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" }
]

3. Apply Policies to Domain (Server Deployment)

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/

4. Testing Authorization Locally

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:

  • Admin deleting post: ✅ Allowed (admin-full-access policy matches)
  • Alice reading own profile: ✅ Allowed (user-self-management policy matches)
  • Alice updating Bob's profile: ❌ Denied (no matching policy)
  • Alice deleting own post: ✅ Allowed (post-ownership policy with owner check)

Scenario 2: Team and CRUD App with Shared Resources

Overview

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.

Architecture

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)

Setup

1. Create Tenant and Domains

# 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

2. Define Policies for 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/.*"

3. Define Policies for App Domain

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"

4. Apply Policies

# 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/

Testing Authorization

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

Scenario 3: Multi-Team Review and Approval

Overview

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.

Architecture

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)

Setup

1. Create Tenant and Domain Hierarchy

# 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

2. Define Compliance Domain Policies

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/.*"

3. Define Operations Domain Policies

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/.*"

4. Define Development Domain Policies

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/.*"

5. Apply Policies to Domains

# 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/

Testing Authorization

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

Workflow Example

A typical workflow in this setup:

  1. Developer writes code: hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/code/feature.rs
  2. Developer proposes config change: hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/app/config/feature-flags.toml
  3. Ops Team reviews and approves config change, updates it
  4. Ops Team approves production deployment: hc://domain/770e8400-e29b-41d4-a716-446655440002/enterprise/deployment/production/app-v2
  5. Compliance Team audits all changes: reads all resources to verify compliance
  6. Ops Team executes deployment: deploy action on production resources
  7. Developer monitors pipeline: logs action on pipeline resources

Best Practices

1. Use Domain Hierarchy for Policy Inheritance

Structure your domains from least-privileged (root) to most-privileged (leaf) so that broader policies propagate down. This prevents accidental privilege escalation.

2. Prefer Deny Policies for Critical Resources

Use explicit deny policies with "deny": true for sensitive resources like production deployments or secrets. Deny policies take precedence over allow policies.

3. Leverage Macros for Dynamic Authorization

Use macros like $current_user(), $resource_owner(), and $current_time() to create flexible, context-aware policies that reduce duplication.

4. Choose the Right Evaluation Engine

  • Fixed: For exact matches (fastest, most secure)
  • Prefix: For hierarchical resources (e.g., hc://domain/<uuid>/app/users/alice/)
  • RegEx: For complex patterns (most flexible, higher computational cost)

Choose the simplest engine that meets your requirements - Fixed and Prefix have lower computational overhead than RegEx.

5. Test Policies Before Production

Always test authorization policies with housectl authz can-i before deploying to production. Create test scenarios covering both allowed and denied cases.

6. Document Policy Intent

Use descriptive policy names and detailed descriptions. Future operators need to understand why a policy exists.

7. Regular Policy Audits

Periodically review policies with compliance team to ensure they still match business requirements and security posture.


Next Steps