{"id":37228973,"url":"https://github.com/it-atelier-gn/desktop-secrets","last_synced_at":"2026-05-21T12:00:56.583Z","repository":{"id":331741612,"uuid":"1131856986","full_name":"it-atelier-gn/desktop-secrets","owner":"it-atelier-gn","description":"Resolve secrets from KeePass, AWS, Azure Key Vault, GCP Secret Manager, HashiCorp Vault, 1Password, macOS Keychain, and Windows Credential Manager into .env templates and subprocess environments — no plaintext on disk.","archived":false,"fork":false,"pushed_at":"2026-05-20T22:38:41.000Z","size":296,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T01:07:34.461Z","etag":null,"topics":["1password","azure-key-vault","desktop","dotenv","hashicorp-vault","keepass","keychain","linux","macos","secret-manager","secrets","security","vault","webauthn","wincred","windows","windows-hello"],"latest_commit_sha":null,"homepage":"https://it-atelier-gn.github.io/desktop-secrets/","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/it-atelier-gn.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":null,"dco":null,"cla":null}},"created_at":"2026-01-10T20:38:57.000Z","updated_at":"2026-05-20T22:38:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/it-atelier-gn/desktop-secrets","commit_stats":null,"previous_names":["it-atelier-gn/tplenv","it-atelier-gn/desktop-secrets"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/it-atelier-gn/desktop-secrets","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-atelier-gn%2Fdesktop-secrets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-atelier-gn%2Fdesktop-secrets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-atelier-gn%2Fdesktop-secrets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-atelier-gn%2Fdesktop-secrets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/it-atelier-gn","download_url":"https://codeload.github.com/it-atelier-gn/desktop-secrets/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/it-atelier-gn%2Fdesktop-secrets/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33299784,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-21T02:57:32.698Z","status":"ssl_error","status_checked_at":"2026-05-21T02:57:31.990Z","response_time":62,"last_error":"SSL_read: 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":["1password","azure-key-vault","desktop","dotenv","hashicorp-vault","keepass","keychain","linux","macos","secret-manager","secrets","security","vault","webauthn","wincred","windows","windows-hello"],"created_at":"2026-01-15T03:30:31.162Z","updated_at":"2026-05-21T12:00:56.569Z","avatar_url":"https://github.com/it-atelier-gn.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DesktopSecrets\n\n![CI/CD Status](https://img.shields.io/github/actions/workflow/status/it-atelier-gn/desktop-secrets/ci.yml)\n![Go](https://img.shields.io/github/go-mod/go-version/it-atelier-gn/desktop-secrets)\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n\nDesktopSecrets is a utility that allows you to remove secrets from your filesystem by transforming them to *Secret References*. It integrates with various *Secret Providers* to make retrieving credentials simple, scriptable, and safe, while minimizing repeated password prompts through configurable caching. \n\n---\n\n## Secret References\n\nA *Secret Reference* is an expression that resolves to a secret value\n\nExamples:\n\n```\nkeepass(C:\\Vaults\\cloud.kdbx|/AWS/Prod/api-key)\nawssm(MyApp/DB|password)\nawsps(/myapp/prod/api-key)\nazkv(mykv/dbpass)\ngcpsm(my-project/api-key)\nvault(secret/data/myapp|password)\nop(Personal/GitHub|token)\nwincred(MyApp/DBPassword)\nkeychain(git.example.com|alice)\nuser(Enter API key)\n```\n\n---\n\n## Commands\n\nDesktopSecrets provides the following commands.\n\n### *tplenv*\n\nResolves secrets inside one or more `.env.tpl` files.\n\nExample:\n\n```properties\nDATABASE_URL=postgresql://localhost:5432/mydb\nAPI_SECRET=keepass($USERPROFILE\\Credentials.kdbx|api-key)\nLOG_LEVEL=debug\n```\n\n`tplenv` prints the fully resolved environment.  \nUse `tplenv run` to execute a command with resolved variables injected.\n\n---\n\n### *getsec*\n\nResolves a single secret reference passed directly as an argument.\n\nExample:\n\n```sh\ngetsec \"API_SECRET=keepass($USERPROFILE\\Credentials.kdbx|api-key)\"\n```\n\n---\n\n## User Provider\n\nPrompts the user to manually enter a secret value.\n\n```\nSECRET_NAME=user(Title shown in prompt)\n```\n\n---\n\n## Windows Credential Manager Provider *(Windows only)*\n\nRetrieves secrets from the Windows Credential Manager — the built-in credential store accessible via **Control Panel › Credential Manager**.\n\n### Format\n\n```properties\nSECRET_NAME=wincred(TARGET)              # password field (default)\nSECRET_NAME=wincred(TARGET|password)     # password field (explicit)\nSECRET_NAME=wincred(TARGET|username)     # username field\n```\n\n- **TARGET** — The credential target name used when storing the credential\n- **Field** — `password` (default) or `username`\n\n### Example\n\n```properties\nDB_PASSWORD=wincred(MyApp/DBPassword)\nDB_USER=wincred(MyApp/DBPassword|username)\n```\n\n---\n\n## AWS Provider\n\nRetrieves secrets from **AWS Secrets Manager** (`awssm`) and **AWS Parameter Store** (`awsps`).\n\nUses the standard AWS credential chain — no extra configuration needed:\n- `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN` env vars\n- `~/.aws/credentials` + `~/.aws/config` (respects `AWS_PROFILE`, `AWS_DEFAULT_REGION`)\n- IAM instance roles, ECS task roles, Web Identity tokens\n\nResolved values are cached in-memory for the configured TTL (same as KeePass).\n\n### AWS Secrets Manager\n\n```properties\n# Raw string secret\nAPI_KEY=awssm(MyApp/ApiKey)\n\n# JSON field extraction\nDB_USER=awssm(MyApp/DB|username)\nDB_PASS=awssm(MyApp/DB|password)\n```\n\n### AWS Parameter Store\n\nSecureString parameters are always decrypted automatically.\n\n```properties\n# Parameter value\nAPI_KEY=awsps(/myapp/prod/api-key)\n\n# JSON field extraction\nDB_HOST=awsps(/myapp/prod/db|host)\n```\n\n\n---\n\n## Azure Key Vault Provider\n\nRetrieves secrets from **Azure Key Vault** (`azkv`).\n\nUses `DefaultAzureCredential` — tries in order: env vars, workload identity, managed identity, Azure CLI (`az login`), Azure PowerShell, Azure Developer CLI. No extra config needed if any of those are set up.\n\n### Format\n\n```properties\nSECRET_NAME=azkv(VAULT/NAME)                    # raw secret value\nSECRET_NAME=azkv(VAULT/NAME|field)              # JSON field extraction\nSECRET_NAME=azkv(https://VAULT.vault.azure.net/NAME)  # full URL form\n```\n\n- **VAULT** — Key Vault name (e.g. `mykv`) or full URL\n- **NAME** — secret name\n- **field** — optional JSON field if the secret value is JSON\n\n### Example\n\n```properties\nDB_PASSWORD=azkv(mykv/db-password)\nDB_USER=azkv(mykv/db-credentials|username)\n```\n\n---\n\n## GCP Secret Manager Provider\n\nRetrieves secrets from **Google Cloud Secret Manager** (`gcpsm`).\n\nUses Application Default Credentials — `GOOGLE_APPLICATION_CREDENTIALS` env var, `gcloud auth application-default login`, attached service account on GCE/GKE/Cloud Run, etc.\n\n### Format\n\n```properties\nSECRET_NAME=gcpsm(PROJECT/NAME)                 # latest version\nSECRET_NAME=gcpsm(PROJECT/NAME/VERSION)         # specific version\nSECRET_NAME=gcpsm(PROJECT/NAME|field)           # JSON field extraction\nSECRET_NAME=gcpsm(projects/P/secrets/N/versions/V)  # fully-qualified form\n```\n\n- **PROJECT** — GCP project ID\n- **NAME** — secret name\n- **VERSION** — numeric version or `latest` (default)\n- **field** — optional JSON field if the secret payload is JSON\n\n### Example\n\n```properties\nAPI_KEY=gcpsm(my-project/api-key)\nDB_PASS=gcpsm(my-project/db-credentials|password)\n```\n\n---\n\n## macOS Keychain Provider *(macOS only)*\n\nRetrieves generic passwords from the **macOS login keychain** via the `security` CLI.\n\nCreate entries with the `security` command or Keychain Access.app:\n\n```sh\nsecurity add-generic-password -s git.example.com -a alice -w 'the-token'\n```\n\n### Format\n\n```properties\nSECRET_NAME=keychain(SERVICE)            # any account matching service\nSECRET_NAME=keychain(SERVICE|ACCOUNT)    # specific account\n```\n\n### Example\n\n```properties\nGIT_TOKEN=keychain(git.example.com|alice)\nAWS_KEY=keychain(aws-prod)\n```\n\n---\n\n## HashiCorp Vault Provider\n\nRetrieves secrets from **HashiCorp Vault** (`vault`).\n\nUses the standard Vault client config — no extra setup needed:\n- `VAULT_ADDR` — Vault server URL\n- `VAULT_TOKEN` — auth token (or file token, AppRole, etc. via standard Vault env vars)\n- `VAULT_NAMESPACE` — namespace for Vault Enterprise\n\n### Format\n\n```properties\nSECRET_NAME=vault(PATH)                 # returns raw JSON or single-key value\nSECRET_NAME=vault(PATH|field)           # extracts a named field\n```\n\n- **PATH** — full Vault path. For KV v2, include `data/` (e.g. `secret/data/myapp`)\n- **field** — optional. If omitted and the secret has a single key, its value is returned; otherwise the full JSON object is returned.\n\n### Example\n\n```properties\n# KV v2 mount at 'secret/'\nDB_PASSWORD=vault(secret/data/myapp|password)\nAPI_TOKEN=vault(secret/data/myapp|api_token)\n\n# KV v1 mount\nLEGACY_KEY=vault(kv/legacy/key)\n```\n\n---\n\n## 1Password Provider\n\nRetrieves secrets from **1Password** via the `op` CLI (`op`).\n\nRequires the [1Password CLI](https://developer.1password.com/docs/cli/) installed and signed in (`op signin`).\n\n### Format\n\n```properties\nSECRET_NAME=op(VAULT/ITEM)              # default `password` field\nSECRET_NAME=op(VAULT/ITEM|field)        # named field (1Password-native, not JSON)\n```\n\nUnder the hood this invokes `op read op://VAULT/ITEM/field`.\n\n### Example\n\n```properties\nGITHUB_TOKEN=op(Personal/GitHub|token)\nDB_PASS=op(Work/Production-DB|password)\n```\n\n---\n\n## KeePass Provider\n\nThe KeePass provider retrieves secrets from `.kdbx` vaults.  \nIt supports:\n\n- absolute paths  \n- wildcard paths (`*` = one level, `**` = any depth)  \n- escaped slashes (`\\/`)  \n- attribute selection  \n- chaining\n\n### Basic Format\n\n```properties\nSECRET_NAME=keepass(VAULT|ENTRY)\n```\n\n- **VAULT** – Path to a KeePass database file (or alias)  \n- **ENTRY** – Title or path pattern\n\n### Entry Lookup Rules\n\n#### 1. Bare titles  \nIf the entry does **not** start with `/`, it is treated as:\n\n```\n**/\u003ctitle\u003e\n```\n\nExample:\n\n```\nkeepass(vault.kdbx|api-key)\n```\n\nSearches for any entry named `api-key` anywhere in the tree.\n\n#### 2. Absolute paths\n\n```\nkeepass(vault.kdbx|/AWS/Prod/api-key)\n```\n\nMatches exactly that path.\n\n#### 3. Wildcards\n\n- `*` matches **one** group level  \n- `**` matches **zero or more** group levels  \n\nExamples:\n\n```\nkeepass(vault.kdbx|/AWS/*/api-key)\nkeepass(vault.kdbx|/AWS/**/api-key)\n```\n\n#### 4. Escaped slashes\n\n```\nkeepass(vault.kdbx|/AWS/Prod/My\\/Key)\n```\n\nMatches an entry titled `My/Key`.\n\n#### 5. Attribute selection\n\n```\nkeepass(vault.kdbx|/AWS/Prod/api-key|UserName)\nkeepass(vault.kdbx|/AWS/Prod/api-key|URL)\nkeepass(vault.kdbx|/AWS/Prod/api-key|Notes)\nkeepass(vault.kdbx|/AWS/Prod/api-key|customField)\n```\n\nAttribute names are case-sensitive. If omitted, the default attribute is the `Password`.\n\n\n### Aliases\n\nAliases for KeePass databases for more flexibility. Aliases are defined in `aliases.yaml` and referenced with `\u0026`.\n\nExample:\n\n```yaml\ncloud: \n  file: C:\\Vaults\\cloud.kdbx \n  master: keepass(\u0026personal|Cloud Master Password)\npersonal: C:\\Vaults\\personal.kdbx\n```\n\nUsage:\n\n```\nMAPS_API_KEY=keepass(\u0026cloud|/Google/Prod/api-key) \nCLAUDE_API_KEY=keepass(\u0026personal|Claude Code API key)\n```\n\n---\n\n### Chaining\n\nKeePass vaults can be unlocked using secrets retrieved from other providers.\n\nExample:\n\n```properties\nSECRET=keepass(VAULT_A[keepass(VAULT_B|MasterPassword)]|/Prod/api-key)\n```\n\nThis:\n\n1. Resolves the inner secret reference  \n2. Uses it as the master password for `VAULT_A`  \n3. Retrieves the final entry\n\nChaining works with all lookup modes, including wildcards and aliases.\n\n---\n\n## Retrieval Approvals\n\nDesktopSecrets ships in two build variants. They are produced from the same source via a Go build tag; pick the one that matches your threat model.\n\n### Normal (default download)\n\n- Default approval mode is **Off** — no prompt, the daemon hands out secrets to any local process that can reach it once the source vault is unlocked.\n- Settings UI exposes two modes: **Off** and **Standard** (an Allow/Deny dialog per retrieval).\n\n### Hardened (`-tags hardened`)\n\n- Only mode available: every retrieval requires an OS-rendered authentication gesture (Windows Hello PIN/fingerprint/face or a hardware security key).\n- The approval mode cannot be lowered from the UI.\n- Build: \n  - `go build -tags hardened -o ...`\n\nTo re-enrol (lost key, new authenticator), delete `webauthn.cred` and restart the daemon.\n\n## Default Configuration Locations\n\n- macOS: `~/Library/Application Support/desktop-secrets`\n- Linux: `$XDG_CONFIG_HOME/desktop-secrets` or `~/.config/desktop-secrets`\n- Windows: `%APPDATA%\\desktop-secrets`\n\n## Environment Overrides\n\n- `DESKTOP_SECRETS_CONFIG_FILE`  \n- `DESKTOP_SECRETS_ALIASES_FILE`  \n- `DESKTOP_SECRETS_KEYFILES_FILE`\n\n---\n\n## Build\n\n### Prerequisites\n\n- Go installed and configured\n\n### Build from Source\n\nWindows:\n\n```pwsh\n# Normal build (default — Off / Standard modes, weaker)\ngo build -o tplenv.exe ./cmd/tplenv\ngo build -o getsec.exe ./cmd/getsec\n\n# Hardened build (Windows Hello / hardware key required for every retrieval)\ngo build -tags hardened -o getsec.exe ./cmd/getsec\n```\n\nLinux:\n\n```sh\ngo build -o tplenv ./cmd/tplenv\ngo build -o getsec ./cmd/getsec\n```\n\n\u003e The hardened build is currently Windows-only — on other platforms it\n\u003e compiles but the OS-factor check at startup will refuse to run.\n\n### Tests\n\nThree test scopes can be combined via build tags:\n\n```pwsh\n# Default — fast unit tests, lite build\ngo test ./...\n\n# Hardened-build unit tests (gate routes to WebAuthn, marker write contract)\ngo test -tags hardened ./...\n\n# Integration tests — build the lite binary into a tempdir, then exercise\n# `--allow-downgrade` and the marker-refusal startup path. Slower (~20s).\ngo test -tags integration ./...\n\n# Everything in one run\ngo test -tags \"hardened integration\" ./...\n```\n\nThe hardened end-to-end path (WebAuthn enrollment + per-retrieval gesture) is **not** covered by automation because no headless way to satisfy a real authenticator exists. Smoke-test that manually after any change to the WebAuthn or enrollment code.\n\n### Usage as library\n```\ngo get github.com/it-atelier-gn/desktop-secrets\n```\n\n```go\nimport (\n  \"os\"\n  desktopsecrets \"github.com/it-atelier-gn/desktop-secrets\"\n)\n\nfunc main() {\n  // Required: allows this binary to be re-launched as the secrets daemon.\n  if desktopsecrets.Init() {\n    os.Exit(0)\n  }\n\n  secret, err := desktopsecrets.ResolveSecret(\"user(DB Password)\")\n  if err != nil {\n    panic(err)\n  }\n  println(secret)\n}\n```\n\n# License\nMIT © 2026 Georg Nelles\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fit-atelier-gn%2Fdesktop-secrets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fit-atelier-gn%2Fdesktop-secrets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fit-atelier-gn%2Fdesktop-secrets/lists"}