{"id":40831891,"url":"https://github.com/josh/tofu-age-encryption","last_synced_at":"2026-01-21T22:26:24.258Z","repository":{"id":313426928,"uuid":"1051397213","full_name":"josh/tofu-age-encryption","owner":"josh","description":"Encrypt OpenTofu state data with age encryption keys","archived":false,"fork":false,"pushed_at":"2025-12-22T18:06:55.000Z","size":127,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-24T07:32:17.115Z","etag":null,"topics":["age-encryption","opentofu"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-09-05T23:21:47.000Z","updated_at":"2025-12-22T18:06:58.000Z","dependencies_parsed_at":"2025-09-06T01:17:22.397Z","dependency_job_id":"79c9e242-11b6-431a-9d53-c8a12f3d9a9e","html_url":"https://github.com/josh/tofu-age-encryption","commit_stats":null,"previous_names":["josh/tofu-age-encryption"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/josh/tofu-age-encryption","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josh%2Ftofu-age-encryption","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josh%2Ftofu-age-encryption/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josh%2Ftofu-age-encryption/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josh%2Ftofu-age-encryption/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josh","download_url":"https://codeload.github.com/josh/tofu-age-encryption/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josh%2Ftofu-age-encryption/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28645548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T21:29:11.980Z","status":"ssl_error","status_checked_at":"2026-01-21T21:24:31.872Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["age-encryption","opentofu"],"created_at":"2026-01-21T22:26:24.194Z","updated_at":"2026-01-21T22:26:24.252Z","avatar_url":"https://github.com/josh.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tofu-age-encryption\n\ntofu-age-encryption provides an external encryption method for [OpenTofu](https://opentofu.org/) using [age](https://age-encryption.org/).\n\nOpenTofu encrypts state with a symmetric key derived from a shared passphrase that every operator must share and rotate together. This project replaces that workflow with age's asymmetric key pairs so operators can keep private keys, specify their own recipients, and rotate access without redistributing a secret.\n\n## Usage\n\n1. Provide the age recipient and identity file using either environment variables or CLI flags:\n\n   Environment variables:\n   - `AGE_IDENTITY_FILE` (alias: `AGE_KEY_FILE`): path to your age identity file\n   - `AGE_IDENTITY` (alias: `AGE_KEY`): age identity string; supports `file:PATH`, `cmd:COMMAND`, and `command:COMMAND`\n   - `AGE_IDENTITY_COMMAND` (alias: `AGE_IDENTITY_CMD`): command whose output is the age identity\n   - `AGE_RECIPIENT` or `AGE_RECIPIENTS`: comma-separated list of age recipients\n   - `AGE_RECIPIENTS_FILE`: path to a file with newline-separated age recipients\n\n   The following `SOPS_`-prefixed variables are also supported as aliases for compatibility with tools that expect them:\n   - `SOPS_AGE_KEY_FILE`: alias for `AGE_IDENTITY_FILE`\n   - `SOPS_AGE_KEY`: age identity string\n   - `SOPS_AGE_KEY_CMD`: alias for `AGE_IDENTITY_COMMAND`\n   - `SOPS_AGE_RECIPIENTS`: alias for `AGE_RECIPIENT`/`AGE_RECIPIENTS` (values from both variables are merged; duplicates are ignored)\n\n   CLI flags:\n\n- `--identity-file`: path to your age identity file\n- `--identity`: age identity string or `file:PATH`, `cmd:COMMAND`, `command:COMMAND`\n- `--identity-command`: command whose output is the age identity\n- `--recipient`: may be provided multiple times or as a comma-separated list of recipients\n- `--recipients-file`: path to a file with newline-separated age recipients\n\n2. Configure OpenTofu to use the external method. For multiple recipients, build the command dynamically:\n\n```hcl\nterraform {\n  encryption {\n    method \"external\" \"age\" {\n      encrypt_command = concat(\n        [\"tofu-age-encryption\", \"--encrypt\"],\n        flatten([for recipient in local.age_recipients : [\"--recipient\", recipient]])\n      )\n      decrypt_command = [\"tofu-age-encryption\", \"--decrypt\", \"--identity\", nonsensitive(var.age_identity)]\n    }\n\n    state {\n      method   = method.external.age\n      enforced = true\n    }\n\n    plan {\n      method   = method.external.age\n      enforced = true\n    }\n  }\n}\n\nlocals {\n  age_recipients = [\n    \"KEY_A\",\n    \"KEY_B\",\n    \"KEY_C\",\n  ]\n}\n\nvariable \"age_identity\" {\n  type      = string\n  sensitive = true\n}\n\nresource \"random_pet\" \"example\" {}\n\noutput \"pet\" {\n  value = random_pet.example.id\n}\n```\n\n3. Run OpenTofu as usual. The state and plan files are encrypted with the given age recipients:\n\n```sh\n$ tofu init\n$ tofu apply\n```\n\n## Manual state recovery\n\nIf the `tofu-age-encryption` binary is unavailable, you can decrypt an existing state file using common command-line tools:\n\n```sh\njq --raw-output '.encrypted_data' terraform.tfstate | base64 --decode \u003estate.age\nage --decrypt --identity key.txt state.age \u003estate.json\njq . state.json\n```\n\nThis process does not require `tofu-age-encryption` and can be used to recover the plain text state for backup or debugging.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosh%2Ftofu-age-encryption","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosh%2Ftofu-age-encryption","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosh%2Ftofu-age-encryption/lists"}