{"id":43870186,"url":"https://github.com/thejefflarson/whisperer","last_synced_at":"2026-02-06T14:27:13.715Z","repository":{"id":274127460,"uuid":"921996985","full_name":"thejefflarson/whisperer","owner":"thejefflarson","description":"A kubernetes operator that syncs secrets across namespaces.","archived":false,"fork":false,"pushed_at":"2025-12-29T00:37:41.000Z","size":361,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-31T18:42:55.375Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/thejefflarson.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":"2025-01-25T03:40:08.000Z","updated_at":"2025-12-29T00:37:38.000Z","dependencies_parsed_at":"2025-02-09T20:27:07.037Z","dependency_job_id":"7de229b4-f9ec-4956-9590-ea6f005bfaf3","html_url":"https://github.com/thejefflarson/whisperer","commit_stats":null,"previous_names":["thejefflarson/whisperer"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/thejefflarson/whisperer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thejefflarson%2Fwhisperer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thejefflarson%2Fwhisperer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thejefflarson%2Fwhisperer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thejefflarson%2Fwhisperer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thejefflarson","download_url":"https://codeload.github.com/thejefflarson/whisperer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thejefflarson%2Fwhisperer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29164854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T12:44:37.655Z","status":"ssl_error","status_checked_at":"2026-02-06T12:44:13.991Z","response_time":59,"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":[],"created_at":"2026-02-06T14:27:12.898Z","updated_at":"2026-02-06T14:27:13.703Z","avatar_url":"https://github.com/thejefflarson.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# whisperer\nA mostly dumb way to make sure every k8s namespace has your pull secrets.\n\n## Ok, what is all this?\n**whisperer** is a kubernetes operator that whispers a secret to another namespaces.\n\nIt's 300 lines of core code in `src/controller.rs`, most of which is handling edgecases, I'd love to know if there are more I haven't thought of.\n\n## Cool so how do I use it?\nWhelp, right now, maybe hold off? I'm a few days away from CICD and a helm chart, but when that's ready, here's how it works.\n\nCreate some namespaces and a secret in a file called `sync.yaml`:\n```yaml\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: source\n---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: target\n---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: target2\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: sync\n  namespace: source\n  labels:\n    whisperer.jeffl.es/sync: \"true\"\n  annotations:\n    whisperer.jeffl.es/namespaces: \"target,target2,missing\"\ndata:\n  secret: dG90YWxseSBhIHN1cGVyIGR1cGVyIHNlY3JldCB5b3Ugc2hvdWxkbid0IHRlbGwgYW55b25lCg==\n```\n\nBoot up a k3ds cluster:\n```\n$ k3d cluster create test\n```\n\nStart the operator:\n```\n$ cargo run\n```\n\nAnd run:\n`kubectl apply -f sync.yaml`\n\nHot diggedy dog, your secrets are synced across namespaces!\n\nJust Look at the `target` and `target2` namespaces! You have secrets that mirror the secret you created:\n```\n$ kubectl get secrets --all-namespaces\nNAMESPACE     NAME                                  TYPE                DATA   AGE\nkube-system   k3d-test-server-0.node-password.k3s   Opaque              1      23m\nkube-system   k3s-serving                           kubernetes.io/tls   2      23m\nsource        sync                                  Opaque              1      70s\ntarget        sync                                  Opaque              1      26s\ntarget2       sync                                  Opaque              1      26s\n```\n\nNEAT-O! No more running `kubectl create secret github ...` in all your namespaces!\n\n## Whelp, that's mildly interesting, how does it work?\n\nI'm glad you asked, the trick is in the labels and annotations on the secret itself:\n```yaml\n  labels:\n    whisperer.jeffl.es/sync: \"true\"\n  annotations:\n    whisperer.jeffl.es/namespaces: \"target,target2,missing\"\n```\n\nThe label `whisperer.jeffl.es/sync` tells the operator to sync the secret to the comma separated namespaces in the annotation `whisperer.jeffl.es/namespaces`. (BTW, **whisperer** will complain about the `missing` namespace here, but if you create it, it'll eventually catch up, like in 300 seconds or so. That's a lot, but maybe that's on you for whispering to an imaginary friend? You do you.)\n\nThere are a couple other niceties as well. You can delete a synced secret and it'll come right back:\n```\n$ kubectl delete secret -n target sync\nsecret \"sync\" deleted\n$ kubectl get secrets --all-namespaces\nNAMESPACE     NAME                                  TYPE                DATA   AGE\nkube-system   k3d-test-server-0.node-password.k3s   Opaque              1      29m\nkube-system   k3s-serving                           kubernetes.io/tls   2      30m\nsource        sync                                  Opaque              1      7m27s\ntarget        sync                                  Opaque              1      26s\ntarget2       sync                                  Opaque              1      6m43s\n```\n\nYou can also search for all your sources of synced secrets:\n```\n$ kubectl get secrets -l \"whisperer.jeffl.es/sync=true\" --all-namespaces\nNAMESPACE   NAME   TYPE     DATA   AGE\nsource      sync   Opaque   1      10m\n```\n\nOr search for \"whispered\" secrets (haha, great pun jeff):\n```\n$ kubectl get secrets -l \"whisperer.jeffl.es/whisper=true\" --all-namespaces\nNAMESPACE   NAME   TYPE     DATA   AGE\ntarget      sync   Opaque   1      3m57s\ntarget2     sync   Opaque   1      10m\n```\n\nOr look for where a particular secret is whispered to:\n```\n$ kubectl get secrets -l \"whisperer.jeffl.es/name=sync,whisperer.jeffl.es/namespace=source\" --all-namespaces\nNAMESPACE   NAME   TYPE     DATA   AGE\ntarget      sync   Opaque   1      5m52s\ntarget2     sync   Opaque   1      12m\n```\n\nNEAT-O KEEN-O!\n\n## Yeah, what's the monitoring sitch like?\n\nWell, it has [tracing](https://docs.rs/tracing/latest/tracing/) so all the environment switches work there, and bonus: it also strives to have [opentelemetry](https://opentelemetry.io/) bells and whistles, and there's a prometheus endpoint if that's your jam, but honestly I only understand like 5% of that stuff.\n\n## Ok, but like namespaces are supposed to be independent.\n\nYeah I know.\n\n## Thanks, I hate it.\n\nCool! I really like you too!  But if it isn't your thing, delete the source secret and everything is gone:\n```\n$ kubectl delete secret -n source sync\nsecret \"sync\" deleted\n$ kubectl get secrets --all-namespaces\nNAMESPACE     NAME                                  TYPE                DATA   AGE\nkube-system   k3d-test-server-0.node-password.k3s   Opaque              1      43m\nkube-system   k3s-serving                           kubernetes.io/tls   2      43m\n```\nHave a wonderful day!\n\n## One more thing: I feel like other folks have made this.\n\nSure, but this is how I wanted it to work. Maybe you do too?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthejefflarson%2Fwhisperer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthejefflarson%2Fwhisperer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthejefflarson%2Fwhisperer/lists"}