$ shellfirm

Creating a Policy

Step-by-step guide to creating your first .shellfirm.yaml team policy

This guide walks you through creating a .shellfirm.yaml policy file for your team.

Quick start

Create a .shellfirm.yaml file in your repository root:

shellfirm policy init

This generates a template policy file that you can customize.

Policy format

Every policy file has this structure:

# .shellfirm.yaml
version: 1

# Optional: deny specific patterns entirely
deny: []

# Optional: override challenge types
overrides: []

# Optional: add custom check patterns
checks: []

# Optional: project-specific context configuration
context: {}

The version field is required and must be 1.

Step-by-step: your first policy

Step 1: Identify your team's risky operations

Think about what operations are dangerous in your project:

  • Force pushing to protected branches?
  • Deleting Kubernetes namespaces?
  • Running database migrations in production?
  • Recursive file deletions in the project root?

Step 2: Create the policy file

# .shellfirm.yaml
version: 1

# Block force pushes entirely
deny:
  - "git:force_push"

# Require explicit "yes" for these operations
overrides:
  - id: "fs:recursively_delete"
    challenge: Yes
  - id: "git:reset_hard"
    challenge: Yes
    on_branches:
      - main
      - production

Step 3: Add custom checks for team-specific commands

If your team has custom deployment scripts or tools, add patterns for them:

checks:
  - id: "team:deploy_prod"
    from: base
    test: "deploy\\s+--env\\s+prod"
    severity: High
    description: "Production deployment - requires explicit approval"
    alternative: "deploy --env staging"
    alternative_info: "Test in staging first"

  - id: "team:db_migrate_prod"
    from: base
    test: "rake\\s+db:migrate.*RAILS_ENV=production"
    severity: Critical
    description: "Production database migration"

Step 4: Configure project context (optional)

Add project-specific protected branches or environment variables:

context:
  protected_branches:
    - staging
    - "release/*"
  production_env_vars:
    DEPLOY_ENV: production

Step 5: Validate the policy

shellfirm policy validate

This checks the YAML syntax and validates all pattern IDs and regex patterns.

Step 6: Commit to your repository

git add .shellfirm.yaml
git commit -m "Add shellfirm team policy"

Complete example for a web application team

# .shellfirm.yaml
version: 1

deny:
  # Never allow these in this repository
  - "git:force_push"
  - "fs:format_filesystem"

overrides:
  # Always require "yes" for destructive file operations
  - id: "fs:recursively_delete"
    challenge: Yes

  # Require "yes" for git reset --hard on protected branches
  - id: "git:reset_hard"
    challenge: Yes
    on_branches:
      - main
      - production
      - staging

  # Require "yes" for database drops
  - id: "database:drop_database"
    challenge: Yes

checks:
  # Team-specific deployment check
  - id: "team:deploy_production"
    from: base
    test: "deploy\\s+--target\\s+production"
    severity: High
    description: "Direct production deployment"
    alternative: "deploy --target staging"
    alternative_info: "Deploy to staging and promote after verification"

  # Prevent running seed data in production
  - id: "team:seed_production"
    from: base
    test: "seed.*production|production.*seed"
    severity: Critical
    description: "Seeding production database can corrupt data"

context:
  protected_branches:
    - main
    - production
    - staging
    - "release/*"
  production_env_vars:
    APP_ENV: production
    DEPLOY_TARGET: live

Field reference

FieldRequiredTypeDescription
versionYesintegerSchema version, must be 1
denyNostring[]Pattern IDs to deny entirely
overridesNoOverride[]Challenge type overrides per pattern
checksNoCheck[]Custom check patterns
contextNoobjectProject-specific context configuration

Override fields

FieldRequiredTypeDescription
idYesstringPattern ID to override (e.g., fs:recursively_delete)
challengeNostringChallenge type: Math, Enter, or Yes
on_branchesNostring[]Only apply on these branches

Custom check fields

FieldRequiredTypeDescription
idYesstringUnique ID in group:name format
fromYesstringCheck group (used for enabling/disabling)
testYesstringRegex pattern to match
severityYesstringInfo, Low, Medium, High, or Critical
descriptionYesstringHuman-readable description
alternativeNostringSafer alternative command
alternative_infoNostringExplanation of why the alternative is safer
filtersNoFilter[]Post-match filters (NotContains, Contains, PathExists)