Skip to content

User guide

End-user documentation from README.md (from AWS SCP Limits through Configuration).

AWS SCP Limits

Constraint Limit
Policy size 10,240 bytes
Statements per SCP 5
SCPs per target (account/OU) 10

Requirements

Python 3.13 or later.

Installation

pip install scpz

Or with uv (recommended):

uv tool install scpz

Container images

Images are built from this repository’s Dockerfile (compatible with Docker and Podman) and published when a GitHub Release is published.

Tagging (both registries): each release tag (for example v0.3.0) is always published; latest is updated only for stable releases (not GitHub pre-releases).

  • ghcr.io/tsjnsn/scpz:<release-tag> and ghcr.io/tsjnsn/scpz:latest (GHCR)
  • tsjnsn/scpz:<release-tag> and tsjnsn/scpz:latest (Docker Hub)

GitHub Container Registry

Published to GitHub Container Registry as ghcr.io/tsjnsn/scpz.

Example (optimize a policy file in the current directory):

docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" ghcr.io/tsjnsn/scpz:latest optimize policy.json

With Podman:

podman run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work:z" ghcr.io/tsjnsn/scpz:latest optimize policy.json

Docker Hub

Published to Docker Hub as tsjnsn/scpz.

# Pin to a release (recommended in production)
docker pull tsjnsn/scpz:v0.3.0

docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" tsjnsn/scpz:v0.3.0 optimize policy.json --dry-run

Build locally

docker build -t scpz:local .
docker run --rm scpz:local --version

Maintainer credentials

The Publish release workflow pushes to GHCR (via GITHUB_TOKEN) and Docker Hub (via repository secrets). Container jobs run only after PyPI publish succeeds so a failed package release does not leave tagged images for an incomplete release. If DOCKERHUB_USERNAME / DOCKERHUB_TOKEN are unset, the Docker Hub job skips with a workflow notice instead of failing the release.

Secret Purpose
DOCKERHUB_USERNAME Docker Hub account with push access to tsjnsn/scpz (must be tsjnsn today; the workflow image name is fixed, not derived from this secret)
DOCKERHUB_TOKEN Docker Hub access token with Read & Write scope for that account

Ensure the Docker Hub repository tsjnsn/scpz exists before the first push.

Usage

Optimize

# Optimize a single file in-place (original saved as policy.json.bak)
scpz optimize policy.json

# Optimize all JSON files in a directory
scpz optimize policies/

# Dry run — show diff + summary without writing
scpz optimize policy.json --dry-run

# Summary only — just show what would change
scpz optimize policy.json --summary-only

# Write to a different file
scpz optimize policy.json --output optimized.json

# Error instead of auto-splitting
scpz optimize policy.json --no-split

Validate

# Validate a single file
scpz validate policy.json

# Validate all JSON files in a directory
scpz validate policies/

Check equivalence

# Ensure optimized output did not broaden permissions (catalog model)
scpz check-equivalence policy.json policy.optimized.json

Optimization Passes

scpz runs the following optimizations in order, repeating until the output stops changing (up to 5 rounds):

  1. Statement merging — Combines statements that share the same Effect, Condition, and Resource into a single statement with a unioned Action list.
  2. Action wildcard compression — Replaces groups of actions sharing a common prefix with wildcard patterns (e.g. s3:GetObject + s3:GetBucketPolicys3:Get*). Uses the bundled AWS action catalog in conservative mode to avoid scope broadening.
  3. Condition merging — Deduplicates condition values and merges equivalent condition blocks.
  4. Resource ARN optimization — Collapses multiple specific ARNs into wildcard patterns (e.g. role/Admin + role/ReadOnlyrole/*).
  5. Redundancy elimination (opt-in) — Removes statements wholly subsumed by another statement in the same policy (Action via wildcards; NotAction only when a non-empty action catalog is configured, mirroring catalog safety for NotAction compression). Enable with redundancyEliminate.enabled: true in scpz.yaml.

When a policy still exceeds limits after optimization, scpz automatically splits it into multiple SCP documents (up to 10 per target).

Configuration

Place a scpz.yaml in your project root (scpz walks up from the input file to find it). See examples/scpz.yaml for a fully-annotated reference.

```yaml apiVersion: scpz.io/v1alpha1 kind: OptimizerConfig metadata: name: default spec: optimizer: actionCompress: mode: conservative # conservative | aggressive redundancyEliminate: enabled: true # opt-in