Monorepo Support
Use multiple .shellfirm.yaml files for different services in a monorepo
In a monorepo, different services may have different safety requirements. shellfirm supports multiple .shellfirm.yaml files placed at different directory levels, so each service can have its own policy.
How discovery works
When shellfirm evaluates a command, it walks up from the current working directory to find the nearest .shellfirm.yaml. The first one found is used.
monorepo/
.shellfirm.yaml ← root policy
services/
api/
.shellfirm.yaml ← API-specific policy
src/
web/
.shellfirm.yaml ← web-specific policy
src/
worker/
src/ ← no local policy, uses root policy
- Working in
services/api/src/-- usesservices/api/.shellfirm.yaml - Working in
services/web/src/-- usesservices/web/.shellfirm.yaml - Working in
services/worker/src/-- uses root.shellfirm.yaml
Example monorepo layout
Root policy (baseline)
# monorepo/.shellfirm.yaml
version: 1
deny:
- "git:force_push"
- "fs:format_filesystem"
overrides:
- id: "fs:recursively_delete"
challenge: Yes
context:
protected_branches:
- main
- production
API service (stricter database rules)
# monorepo/services/api/.shellfirm.yaml
version: 1
deny:
- "database:drop_database"
- "database:truncate_table"
checks:
- id: "api:migrate_prod"
from: base
test: "migrate.*--env\\s+production"
severity: Critical
description: "Production database migration for API service"
overrides:
- id: "database:drop_database"
challenge: Yes
Web frontend (fewer restrictions)
# monorepo/services/web/.shellfirm.yaml
version: 1
checks:
- id: "web:deploy_cdn"
from: base
test: "deploy.*--cdn.*production"
severity: High
description: "CDN deployment affects all users immediately"
Infrastructure service (maximum protection)
# monorepo/infrastructure/.shellfirm.yaml
version: 1
deny:
- "terraform:destroy"
- "kubernetes:delete_namespace"
- "aws:delete_stack"
overrides:
- id: "terraform:apply"
challenge: Yes
- id: "kubernetes:scale_to_zero"
challenge: Yes
checks:
- id: "infra:delete_rds"
from: base
test: "aws\\s+rds\\s+delete"
severity: Critical
description: "Deleting an RDS instance is irreversible"
Policy interaction with global settings
The found .shellfirm.yaml is merged with the user's global ~/.shellfirm/settings.yaml:
- Global settings provide the baseline (challenge type, enabled groups, etc.)
- Project policy adds restrictions on top (deny lists, overrides, custom checks)
The project policy cannot weaken global settings. It can only add to them.
Best practices for monorepos
Use a root-level baseline policy
Place a .shellfirm.yaml at the monorepo root with rules that apply to all services. Service-specific policies then add to this baseline.
Keep service policies focused
Each service policy should only contain rules specific to that service. Shared rules belong in the root policy.
Document your policy structure
Add a note in your repository README or contributing guide explaining where policies are and what they cover:
# Safety Policies
- `/.shellfirm.yaml` - Baseline rules (git, filesystem)
- `/services/api/.shellfirm.yaml` - Database and migration rules
- `/infrastructure/.shellfirm.yaml` - Cloud and Kubernetes rules
Validate all policies in CI
Run shellfirm policy validate from each directory that has a policy:
# In CI
for policy in $(find . -name ".shellfirm.yaml"); do
dir=$(dirname "$policy")
cd "$dir"
shellfirm policy validate
cd -
done