{"id":42166472,"url":"https://github.com/julian7/redact","last_synced_at":"2026-01-26T21:06:20.217Z","repository":{"id":57510682,"uuid":"194438192","full_name":"julian7/redact","owner":"julian7","description":"Data encryptor for git","archived":false,"fork":false,"pushed_at":"2025-07-12T13:26:19.000Z","size":462,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-15T04:56:11.854Z","etag":null,"topics":["encryption","git"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/julian7.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-BlueOak.md","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}},"created_at":"2019-06-29T18:44:31.000Z","updated_at":"2025-07-12T13:26:23.000Z","dependencies_parsed_at":"2023-12-08T17:29:34.820Z","dependency_job_id":"776f2c01-326b-4e00-a539-a0d2f7476418","html_url":"https://github.com/julian7/redact","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/julian7/redact","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fredact","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fredact/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fredact/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fredact/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/julian7","download_url":"https://codeload.github.com/julian7/redact/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fredact/sbom","scorecard":{"id":541367,"data":{"date":"2025-08-11","repo":{"name":"github.com/julian7/redact","commit":"dc1acdb5d1290acbf64192c311512fbd93e953f8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE-BlueOak.md:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.10.0 not signed: https://api.github.com/repos/julian7/redact/releases/212001032","Warn: release artifact v0.9.0 not signed: https://api.github.com/repos/julian7/redact/releases/148812820","Warn: release artifact v0.8.0 not signed: https://api.github.com/repos/julian7/redact/releases/133200476","Warn: release artifact v0.7.1 not signed: https://api.github.com/repos/julian7/redact/releases/78840480","Warn: release artifact v0.7.0 not signed: https://api.github.com/repos/julian7/redact/releases/78796319","Warn: release artifact v0.10.0 does not have provenance: https://api.github.com/repos/julian7/redact/releases/212001032","Warn: release artifact v0.9.0 does not have provenance: https://api.github.com/repos/julian7/redact/releases/148812820","Warn: release artifact v0.8.0 does not have provenance: https://api.github.com/repos/julian7/redact/releases/133200476","Warn: release artifact v0.7.1 does not have provenance: https://api.github.com/repos/julian7/redact/releases/78840480","Warn: release artifact v0.7.0 does not have provenance: https://api.github.com/repos/julian7/redact/releases/78796319"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T08:16:57.568Z","repository_id":57510682,"created_at":"2025-08-20T08:16:57.568Z","updated_at":"2025-08-20T08:16:57.568Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28788140,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T21:02:48.137Z","status":"ssl_error","status_checked_at":"2026-01-26T21:01:13.039Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":["encryption","git"],"created_at":"2026-01-26T21:06:19.696Z","updated_at":"2026-01-26T21:06:20.210Z","avatar_url":"https://github.com/julian7.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Redact, data encryptor in git\n\n**PLEASE NOTE**: this project is not released yet, and it's under testing. Use it for your own risk.\n\nRedact allows you to store sensitive data in a git repository by encrypting / decrypting it on-the-fly.\n\nThe project is very similar to [git-crypt](https://github.com/AGWA/git-crypt), [transcrypt](https://github.com/elasticdog/transcrypt), and [git-secret](https://github.com/sobolevn/git-secret).\n\n## WARNING\n\nIn nominal cases, you should never store your secrets in a git repository. Use a different tool for that. However, when the sensitivity levels of secret / plaintext files are not too far away (like general and specific settings for a closed source application), it's a nice touch to have an extra layer of security on top of what a git service can provide.\n\n## Intro\n\nIn order to store secrets in a git repository, you have to initialize it:\n\n```shell\n$ redact init\nNew repo key created: .git/redact (#1 c90014bb)\n```\n\nThis creates a secret key into `.git/redact` directory, and it also sets up diff / filter attributes for later use.\n\nThen, tell the repo which files are going to be encrypted. Create a `.gitattributes` file like this one:\n\n```text\n*.key filter=redact diff=redact\n```\n\nThis file will instruct git to encrypt every file with the `.key` extension. Let's create a secret file called `private.key`:\n\n```text\nSecret Information\n```\n\nThen, add them to version control:\n\n```shell\n$ git add --all\n$ git status -sb\n## No commits yet on main\nA  .gitattributes\nA  private.key\n$ git commit -m initial\n[main (root-commit) 0b9eb27] initial\n 2 files changed, 2 insertions(+)\n create mode 100644 .gitattributes\n create mode 100644 private.key\n```\n\nAt this point we have an encrypted file in history:\n\n```shell\n$ redact status\n            .gitattributes\nencrypted:  private.key\nThere are 0 files to be fixed.\nThere are 0 files to be re-encrypted.\n```\n\nJust check whether it has been encrypted:\n\n```text\n$ git ls-tree HEAD\n100644 blob 8fa0eac4c7076b9f10c99460c2feaf905f0e5cd2  .gitattributes\n100644 blob a24b3ae46b388e8ca0011a13d79f94cdd65feb35  private.key\n$ git cat-file blob a24b3ae46b388e8ca0011a13d79f94cdd65feb35 | hexdump -C\n00000000  00 52 45 44 41 43 54 45  44 00 00 00 00 00 00 00  |.REDACTED.......|\n00000010  00 01 3f ef ae 3f 90 aa  73 cd 39 8c 89 1c 2e e8  |..?..?..s.9.....|\n00000020  dc 20 22 66 ea 7a 97 7f  b2 86 38 7e 40 fc 05 e5  |. \"f.z....8~@...|\n00000030  5a 44 41 c5 37 8e c4 8c  02 28 a4 ca 38 6a 76 7b  |ZDA.7....(..8jv{|\n00000040  08                                                |.|\n00000041\n$ git cat-file blob a24b3ae46b388e8ca0011a13d79f94cdd65feb35 | redact git smudge\nSecret Information\n$ cat private.key\nSecret Information\n```\n\nAdd contributors:\n\n```text\n$ redact openpgp grant keybase.io/julian7\nKeyID: BDE0F1CE, fingerprint: 1857918cd0b4d303071d6624466cbb98bde0f1ce\n  identity: keybase.io/julian7 \u003cjs@iksz.hu\u003e, expires: 2027-10-08 18:45:56 +0200 CEST\n  identity: keybase.io/julian7 \u003cjulian7@keybase.io\u003e, expires: 2025-01-07 22:35:45 +0100 CET\nINFO[0000] Added 1 key. Don't forget to commit exchange files to the repository.\n$ git add --all\n$ git status -sb\n## main\nA  .redact/.gitattributes\nA  .redact/1857918cd0b4d303071d6624466cbb98bde0f1ce.asc\nA  .redact/1857918cd0b4d303071d6624466cbb98bde0f1ce.key\n```\n\n## What Redact provides, what other tools don't?\n\n* Redact is written in go, and as such, it can bring encryption into environments with no bash (???), and can be cross-compiled.\n* it uses OpenPGP for key exchange (almost like git-crypt)\n* it requires GnuPG only for unlocking, all OpenPGP operations are handled internally\n* it stores OpenPGP keys of collaborators next to their encrypted secret key copies\n* it supports key rotation (like transcrypt)\n* auto-generates key (not like transcrypt)\n* only dependencies are git and GnuPG\n* it doesn't commit into the repository automatically\n\n## Security\n\nKeys are prepared for AES-256 (256 bits) and HMAC-256 (512 bits). The system is able to store multiple keys, with different epoch numbers. All keys are usable for decryption, but by default only the latest key is used for encryption.\n\nFile encoding uses AES256-GCM96 encoding. Encryption nonce is calculated from the plaintext's HMAC key, taking the first 96 bits. Encrypted file stores this calculated nonce, and ciphertext. During decoding, the saved nonce is checked against calculated HMAC, whether its first 96 bits are matching.\n\nAlternatively, ChaCha20-Poly1305 can be used in place of AES256-GCM96. According to the [Automatic cipher suite ordering in crypto/tls](https://go.dev/blog/tls-cipher-suites) blog entry in the Go Blog, AES128-GCM96 is not suitable for the post-quantumcomputing world, which is still just fine for TLS encryption, but not in our case. However, AES256-GCM96 provides adequate protection for that case too. On the other hand, the consensus on using AES+GCM with no hardware acceleration is pretty much discouraged as it's very hard to implement it effectively and securely.\n\nTherefore, if encryption is used in environments which lacks hardware AES acceleration, it's more secure to use ChaCha20-Poly1305 instead.\n\nTo switch, either set `REDACT_GIT_CLEAN_TYPE` environment variable, or set `git.clean.type` in configuration to `chacha20-poly1305` (case insensitive).\n\n## Subcommands\n\n* key: secret key commands:\n  * init: initializes secret key\n  * export: exports secret key in a PEM-encoded (readable) format\n  * generate: generates new secret key\n  * info (default): shows secret key info\n  * list: lists all keys\n  * save: saves secret key in Key Exchange (both OpenPGP and extensions)\n* lock: locks repository (deletes local key and removes diff/filter configs)\n* unlock: unlocks repository with local key\n  * gpg: unlocks repository with GPG-encrypted key from key exchange\n* openpgp/gpg: OpenPGP key exchange commands\n  * ls/list: list user access\n  * grant: add OpenPGP key access\n  * update: re-encrypt secret key with OpenPGP keys (not implemented yet)\n* git: git filter commands\n  * clean: acts as clean filter for git\n  * diff: acts as diff filter for git\n  * smudge: acts as smudge filter for git\n* status: list files' encryption status\n* ext: extension management\n  * add: adds extension\n  * rm/del: removes extension\n  * list: lists all extensions\n  * update: updates extension configuration\n\nSee [the to do](TODO.md) file for details.\n\n## Revoke access\n\nWhen you lose trust of someone, there is one thing we can't do: we can't revoke\nhistorical access to secrets stored in the repository. We can do something about the future though:\n\n* remove encrypted key from the key exchange folder. This will stop the user from recreating the secret key in the future.\n* generate a new key: rotate secret key. You can re-encrypt secret files as they are, but since usually the untrusted party already knows about the secrets, they can easily figure out the newly encrypted files are indeed having the same content. This can possibly help them learning about the new secret key.\n* replacing secrets: when encrypted files are supposed to be exposed, the best thing we can do is not just replacing their encryptions, but replacing secrets too. For example, if encrypted files are secret parts of key pairs (like a TLS certificate), we might want to revoke the full certificate altogether, generating new ones.\n\nAs always, play safe, and revoke all secrets if there is any chance it can cause damage.\n\n## Extensions\n\nRedact can store secret keys externally, with a simple extension mechanism. It allows managing multiple redact keys in a controlled manner. This package ships AWS Parameter Store and Azure Key Vault extensions, but implementing such an extension is very straightforward.\n\nIn general, all extension configuration goes to `\u003crepo root\u003e/.redact/config.json`, which should be committed to the repository, and should not be encrypted. Every extension configuration can hold the following information:\n\n* name: name of the extension\n* command path: command to execute the extension, defaults to `redact-ext-\u003cname\u003e`\n* options: `key=value` pairs, which are passed to the extension command as command-line arguments\n\nAn extension has to implement the following commands:\n\n* `list`: basic information about the extension and its configuration\n* `get`: retrieves an exported secret key to standard output\n* `put`: stores an exported secret key read from standard input\n\nConfigured extensions are invoked as the following:\n\n```\n\u003ccommand path\u003e \u003ccommand\u003e \u003coption1\u003e=\u003cvalue1\u003e [\u003coption2\u003e=\u003cvalue2\u003e ...]\n```\n\n## Configuration with environment variables\n\nA couple of options are configurable with environment variables too, which are taking precedence over command-line options. You can set the following variables:\n\n* `REDACT_GIT_CLEAN_EPOCH`: sets `--epoch` option for `redact git clean` subcommand\n* `REDACT_GIT_CLEAN_TYPE`: sets `--type` option for `redact git clean` subcommand\n* `REDACT_LOG_LEVEL`: sets `--verbosity` global option\n* `REDACT_LOG_LEVEL`: sets `--logfile` global option\n* `REDACT_STRICT`: sets `--strict-permissions` global option\n* `REDACT_UNLOCK_EXPORTED_KEY`: sets `--exported-key` option for `redact unlock` subcommand\n* `REDACT_UNLOCK_GPG_KEY`: sets `--gpgkey` option for `redact unlock gpg` subcommand\n* `REDACT_UNLOCK_KEY`: sets `--key` option for `redact unlock` subcommand\n\n## Any issues?\n\nOpen a ticket, perhaps a pull request. We support [GitHub Flow](https://guides.github.com/introduction/flow/). You might want to [fork](https://guides.github.com/activities/forking/) this project first.\n\n## Licensing\n\nSPDX-License-Identifier: BlueOak-1.0.0 OR MIT\n\nThis software is licensed under two licenses of your choice: Blue Oak Public License 1.0, or MIT Public License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian7%2Fredact","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulian7%2Fredact","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian7%2Fredact/lists"}