{"id":45261799,"url":"https://github.com/yawn/offkey","last_synced_at":"2026-02-21T00:28:20.101Z","repository":{"id":48998361,"uuid":"517125205","full_name":"yawn/offkey","owner":"yawn","description":"Print age encrypted small secrets for offline recovery","archived":false,"fork":false,"pushed_at":"2025-10-07T14:37:29.000Z","size":162,"stargazers_count":23,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-07T16:26:32.478Z","etag":null,"topics":["age","cryptography","encryption","paperkey"],"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/yawn.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}},"created_at":"2022-07-23T17:42:33.000Z","updated_at":"2025-10-07T14:55:56.000Z","dependencies_parsed_at":"2024-06-20T06:08:58.789Z","dependency_job_id":"46b3acce-908c-41d4-948d-a4b8e448001f","html_url":"https://github.com/yawn/offkey","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/yawn/offkey","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yawn%2Foffkey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yawn%2Foffkey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yawn%2Foffkey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yawn%2Foffkey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yawn","download_url":"https://codeload.github.com/yawn/offkey/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yawn%2Foffkey/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29668686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T00:11:43.526Z","status":"ssl_error","status_checked_at":"2026-02-20T23:52:33.807Z","response_time":59,"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","cryptography","encryption","paperkey"],"created_at":"2026-02-21T00:28:19.324Z","updated_at":"2026-02-21T00:28:20.087Z","avatar_url":"https://github.com/yawn.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Print age encrypted small secrets for offline recovery (`offkey`)\n\n[![Go](https://github.com/yawn/offkey/actions/workflows/go.yml/badge.svg)](https://github.com/yawn/offkey/actions/workflows/go.yml)\n\n`offkey` provides a workflow to render small sized secrets (e.g. secret keys) as encrypted QR codes, suitable for printing, subsequent long term storage and recovery using standard hardware (e.g. mobile phones). Currently the maximum number of bytes for a secret is `1489`.\n\nThe encryption part of `offkey` is handled through [age](https://github.com/FiloSottile/age) encryption and encoding.\n\n`offkey` is inspired by the ideas of [Paperkey](https://wiki.archlinux.org/title/Paperkey).\n\n## Usage\n\n`offkey` reads secrets from `stdin` and opens the standard browser. The URL in this browser renders the encrypted and QR encoded secret. Afterwards (or after 5m have passed) `offkey` exits.\n\nThe following (optional) flags can be passed:\n\n- `-d=Description`: a description of the secret that is rendered as HTML title\n- `-o=false`: do not open the URL in a standard browser - instead the URL is printed out like this: `Open \"http://127.0.0.1:53550?token=yPEO6bpL4ufjiyRZRtyWcQ\" in your browser`\n\n### Creating a secret\n\n`echo \"this is my very secret message from 1658944996\" | offkey`\n\nThis opens the standard browser and points it to `http://127.0.0.1:PORT/?token=TOKEN`. Both `PORT` and `TOKEN` parameter are randomized, with `PORT` matching any open TCP port.\n\n![secret in the browser](.readme/secret-with-passphrase.png)\n\nThe resulting page in the browser contains a randomly generated passphrase (following the conventions of `age`) e.g. `adjust-skin-wealth-suit-jelly-shuffle-vehicle-sunny-symptom-bundle`.\n\n### Printing the secret\n\nWhen printing this secret, CSS hides the passphrase and instead replaces it with hints on it's structure:\n\n![secret in the printer](.readme/secret-for-printing.png)\n\nAfter printing, the passphrase needs to be written (with a permanent pen) on the printout. `offkey` exits automatically after serving the secret (or after 5 minutes when not serving the secret).\n\n### Recovering a secret\n\nThere are various options for QR code scanning, e.g. using  using [zbar](https://formulae.brew.sh/formula/zbar) to extract the QR code content from a file, perhaps taken by [imagesnap](https://formulae.brew.sh/formula/imagesnap) (both on Mac).\n\nAnother alternative is to use an iOS workflow and an iOS devices to scan the QR code and send it's contents to a Mac using AirDrop:\n\n![recovery with shortcuts](.readme/shortcuts.png)\n\nThe content of the QR code is an armored `age` file:\n\n```\n-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBVU3dZa3MycTIxa2pkV3hP\nWEplK0VnIDE4CmF1bndqS1BLTTJNT2hzRHNSelpJVFkxZkdCQjBvNk52RE10eGgz\naW4zeG8KLS0tIHB3YkZtUHFzdGJ4RUNscm1hcVBEdk03THd0WnI2M1d0YUtrQmF6\nN1hVTUEKPdb2R2KULhdK+LgCcxC/ZahAVYqJsJG+N2MX7OGAXtfSTMw8ct8G/MKv\nkz7ITT+um/g0gA5kegCH4ynQi171PXOXzZD2XrJ6mwNMzHOy8A==\n-----END AGE ENCRYPTED FILE-----\n```\n\nThis can be decrypted by just calling age e.g. `age --decrypt secret.age`. After entering the passphrase `adjust-skin-wealth-suit-jelly-shuffle-vehicle-sunny-symptom-bundle`, the secret is recovered:\n\n```\nage --decrypt secret.age \nEnter passphrase: \nthis is my very secret message from 1658944996\n```\n\n## Contributions\n\nContributions are welcome! Inspired by [kivikakk](https://github.com/kivikakk) / Peter Hintjens I am trying to apply [Optimistic Merging](http://hintjens.com/blog:106).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyawn%2Foffkey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyawn%2Foffkey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyawn%2Foffkey/lists"}