PlanSeal
Cryptographic approval for saved infrastructure plans.
PlanSeal proves that the Terraform or OpenTofu plan being offered for execution is exactly the plan a human approved: same file, same Git revision, same provider lockfile, and same resource-action scope.
saved plan -> sanitized evidence -> owner signature -> exact-input verification -> apply
PlanSeal is local-first, has no service or account, does not manage cloud
credentials, and does not persist raw plan JSON. Apply is preview-only unless
the operator explicitly supplies --execute.
[!WARNING] PlanSeal is alpha software. It has not received an independent security audit. Do not treat it as the only control protecting production systems.
Why
Saved plans separate review from execution, which is useful for automation and AI-assisted operations. That separation creates a precise question:
Is this still the exact artifact I reviewed?
An approval in chat or a CI button does not, by itself, bind the approval to the plan bytes, source revision, dependency lock, and action list. PlanSeal creates and verifies that binding.
Use PlanSeal when
- a saved Terraform or OpenTofu plan is reviewed separately from execution;
- an owner needs to authorize exact plan bytes and a bounded action scope;
- a local or owner-operated workflow should remain independent of a hosted approval service; or
- automation or an AI agent prepares a plan but must not approve its own work.
Choose another control when
- a managed platform already provides the complete plan/apply approval lifecycle you need;
- you require multi-party approval, hardware-backed signing, remote identity, or centralized policy enforcement; or
- you cannot protect saved plan files and the local owner key appropriately.
Quick start
Requirements: Python 3.11+, Git, and either OpenTofu or Terraform.
pipx install planseal
planseal keygen \
--private-key ~/.config/planseal/owner-private.pem \
--public-key ~/.config/planseal/owner-public.pem
tofu plan -out=change.tfplan
planseal inspect change.tfplan \
--repo . \
--lockfile .terraform.lock.hcl \
--output evidence.json
Review the sanitized evidence, copy its digest from the inspect output, and
approve only the action classes you intend:
planseal approve evidence.json \
--private-key ~/.config/planseal/owner-private.pem \
--allow create,update \
--confirm sha256:REPLACE_WITH_EVIDENCE_DIGEST \
--output certificate.json
Verify all bindings and preview the fixed apply command:
planseal verify \
--evidence evidence.json \
--certificate certificate.json \
--public-key ~/.config/planseal/owner-public.pem \
--plan change.tfplan \
--repo . \
--lockfile .terraform.lock.hcl
planseal apply \
--evidence evidence.json \
--certificate certificate.json \
--public-key ~/.config/planseal/owner-public.pem \
--plan change.tfplan \
--repo . \
--lockfile .terraform.lock.hcl
The last command is a preview. Add --execute only after reviewing its JSON
output. Execution consumes the certificate nonce before starting the saved
plan, so the certificate cannot be reused.
For a credential-free walkthrough, use the minimal synthetic example. To install directly from source instead, see Getting Started.
What evidence contains
{
"actions": [
{"actions": ["update"], "address": "module.edge.example_resource.policy"}
],
"lockfile_checksum": "sha256:...",
"plan_checksum": "sha256:...",
"plan_id": "change.tfplan",
"schema_version": 1,
"source_revision_digest": "sha256:...",
"tool": "opentofu"
}
It intentionally excludes plan values, provider configuration, state, environment variables, credentials, command output, and absolute paths. Resource addresses may still reveal naming conventions; treat evidence as operational metadata.
Security properties
- Ed25519 signatures over canonical JSON.
- Five-minute maximum certificate lifetime.
- Exact plan, source revision, lockfile, and action-scope binding.
- Explicit approval for delete and replacement actions.
- Clean tracked Git worktree required at inspection and verification.
- No shell invocation.
- Preview-first execution.
- SQLite-backed one-time certificate consumption.
- Sanitized machine-readable errors.
Read the threat model before using PlanSeal around sensitive infrastructure.
Project status
PlanSeal v0.1 is an intentionally narrow proof of the exact-artifact approval protocol. Planned follow-up work includes encrypted OS-backed key storage, hardware-backed owner presence, remote receiver profiles, and signed receipts. Those features are not part of the current security claim.
Documentation
Start at the documentation index.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md, SECURITY.md, and CODE_OF_CONDUCT.md.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file planseal-0.1.2.tar.gz.
File metadata
- Download URL: planseal-0.1.2.tar.gz
- Upload date:
- Size: 32.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75575a56ff689b9d13310de2a3ab0479f1ba5034ab938d87eaafbf169b5d4e7c
|
|
| MD5 |
5f90d748771ae685bc834e1aae3a24d3
|
|
| BLAKE2b-256 |
8b0b8aeff5e2cfdf9a6b3ebd0cdf5a1354096617b888c4bf8d0f8fda546c6b9f
|
Provenance
The following attestation bundles were made for planseal-0.1.2.tar.gz:
Publisher:
release.yml on haithdoan/planseal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
planseal-0.1.2.tar.gz -
Subject digest:
75575a56ff689b9d13310de2a3ab0479f1ba5034ab938d87eaafbf169b5d4e7c - Sigstore transparency entry: 2735904375
- Sigstore integration time:
-
Permalink:
haithdoan/planseal@0829d9a074ab1e79d3da1ce79d475cae6b2de380 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/haithdoan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0829d9a074ab1e79d3da1ce79d475cae6b2de380 -
Trigger Event:
push
-
Statement type:
File details
Details for the file planseal-0.1.2-py3-none-any.whl.
File metadata
- Download URL: planseal-0.1.2-py3-none-any.whl
- Upload date:
- Size: 16.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57fff65b5af27f18ff5d24505cfa638ec6b915a707f6e5fbec29457a9b897e9d
|
|
| MD5 |
58e301cdd35d11baa9fc069c065a2e13
|
|
| BLAKE2b-256 |
5282c312ca4c525be3c4b8a0421ce3911fa64437323be676dc88c38de561d18b
|
Provenance
The following attestation bundles were made for planseal-0.1.2-py3-none-any.whl:
Publisher:
release.yml on haithdoan/planseal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
planseal-0.1.2-py3-none-any.whl -
Subject digest:
57fff65b5af27f18ff5d24505cfa638ec6b915a707f6e5fbec29457a9b897e9d - Sigstore transparency entry: 2735904424
- Sigstore integration time:
-
Permalink:
haithdoan/planseal@0829d9a074ab1e79d3da1ce79d475cae6b2de380 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/haithdoan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0829d9a074ab1e79d3da1ce79d475cae6b2de380 -
Trigger Event:
push
-
Statement type: