scpz
Intelligently optimize AWS Service Control Policy (SCP) JSONs to fit within AWS's strict limits.
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>andghcr.io/tsjnsn/scpz:latest(GHCR)tsjnsn/scpz:<release-tag>andtsjnsn/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):
- Statement merging — Combines statements that share the same Effect, Condition, and Resource into a single statement with a unioned Action list.
- Action wildcard compression — Replaces groups of actions sharing a common prefix with wildcard patterns (e.g.
s3:GetObject+s3:GetBucketPolicy→s3:Get*). Uses the bundled AWS action catalog in conservative mode to avoid scope broadening. - Condition merging — Deduplicates condition values and merges equivalent condition blocks.
- Resource ARN optimization — Collapses multiple specific ARNs into wildcard patterns (e.g.
role/Admin+role/ReadOnly→role/*). - Redundancy elimination (opt-in) — Removes statements wholly subsumed by another statement in the same policy (
Actionvia wildcards;NotActiononly when a non-empty action catalog is configured, mirroring catalog safety forNotActioncompression). Enable withredundancyEliminate.enabled: trueinscpz.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.
apiVersion: scpz.io/v1alpha1
kind: OptimizerConfig
metadata:
name: default
spec:
optimizer:
actionCompress:
mode: conservative # conservative | aggressive
redundancyEliminate:
enabled: true # opt-in
# Print the JSON Schema for editor validation
scpz print-schema
Development
# Install with dev dependencies
uv sync --dev
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=scpz
# Run a specific test file
uv run pytest tests/test_actions.py -v
# Regenerate the committed schema after model changes
uv run scpz print-schema -o schema/OptimizerConfig.json
License
MIT
Metadata
Release files for scpz 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| scpz-0.3.1.tar.gz | 245.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scpz-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 396.4 kB
Release files / scpz-0.3.1.tar.gz
| Download URL | scpz-0.3.1.tar.gz |
|---|---|
| Size | 245.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
76eb25cbd7db4850c81e4af3381e39d4e291094ee1c8c0fe46299b08b08ffe52
|
|
BLAKE2b-256 checksum How to use checksums |
dbdc66b5ea20ee073e413333abb77987b538630381e0de8d78e59f6657190e87
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 24, 2026.
Transparency logRelease files / scpz-0.3.1-py3-none-any.whl
| Download URL | scpz-0.3.1-py3-none-any.whl |
|---|---|
| Size | 150.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b506b0539212bd45183228462024c49f3843c3d169b333ceca71c160d4cfcfe7
|
|
BLAKE2b-256 checksum How to use checksums |
048853bdd589614767c888cc16eb53c30f34b72a37cb9de5ea1dfc939404d233
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 24, 2026.
Transparency log