Show HN: kiln – Git-native, decentralized secret management using age

Show HN (score: 5)
Found: July 17, 2025
ID: 366

Description

Other
Show HN: kiln – Git-native, decentralized secret management using age Hi HN, I've been building this tool for the past couple of weeks to solve a problem that seems universal across development teams: sharing environment variables securely.

You know the drill - someone needs the staging database URL, so it gets shared over chat. Production API keys end up in plaintext files. Or you set up some complex secret management system that becomes a single point of failure during critical deployments.

At Zerodha, we're a stock broker with strict regulatory requirements. Our infrastructure needs to be auditable, and our data must stay with us for instant recovery. But the deeper issue was that every solution we tried made deployments dependent on external services.

We tried GitLab CI's built-in secrets, but they're stored unencrypted and only repository maintainers can access them. HashiCorp Vault was too complex to manage with painful ACL setup, plus it's now crippled by their BSL license change. AWS Secrets Manager would create the vendor lock-in we wanted to avoid.

The breaking point came when we wanted to manage secrets through Terraform for idempotency and better infrastructure-as-code practices. But Terraform has no built-in way to encrypt secrets without relying on external providers. We could either store secrets in plaintext in our Terraform configs or add yet another external dependency to our deployment pipeline.

That's when I had the idea: what if we could inject encrypted environment variables directly into Terraform, so anyone with the right key could deploy without hunting down secrets from different systems? As I iterated through this idea, I realized the same pattern would work for any application - from personal projects to team deployments.

So I built kiln. It encrypts environment variables using age encryption into files that live alongside your code. No servers, no network calls, no external dependencies. Each team member gets their own key, and you control access per environment.

Here's how it works:

  # Generate a new age key, or use your existing SSH keys
  kiln init key
  
  # Initialize with your team's public keys
  kiln init config --recipients "alice=$(curl https://gitlab.company/alice.keys)" --recipients "me=$(cat ~/.ssh/id_ed25519.pub)"
  
  # Set secrets (prompts securely, never shows in terminal)
  kiln set DATABASE_URL
  kiln set API_KEY
  
  # Run your app with decrypted environment
  kiln run npm start
  

  # These encrypted files are safe to commit
  git add .kiln.env kiln.toml

Why not SOPS? SOPS is great for general file encryption, but kiln is built specifically for the environment variable workflow. It has commands like "run", "export", and built-in team management. Think "SOPS for .env files" with a focus on developer UX.

Why not raw age encryption? Age is perfect for the crypto layer, but terrible for day-to-day team workflows. Try managing 20 team members across 5 environments with raw age commands - you'll go insane. kiln handles the orchestration.

As for technical details, kiln:

- Uses age encryption (modern, audited, simple)

- Works with existing SSH keys or generates new age keys

- Role-based access via TOML configuration

- Single, cross-platform Go binary

- Zero network dependencies - everything works offline

- MIT licensed

The game-changer: secrets travel with code. No more "can someone send me the staging secrets?" in chat. No more broken deploys because the secret service is down. No more hoping your vendor doesn't change their pricing or licensing.

Try it out - I'm confident it'll help improve your team's deployment workflows. Feel free to ask me any questions!

GitHub: https://github.com/thunderbottom/kiln

Docs: https://kiln.sh

Or install now: go install github.com/thunderbottom/kiln@latest

More from Show

Show HN: KeyEnv – CLI-first secrets manager for dev teams (Rust)

Show HN: KeyEnv – CLI-first secrets manager for dev teams (Rust) Hi HN,<p>I built KeyEnv because I was tired of the &quot;can you Slack me the Stripe key?&quot; workflow.<p><pre><code> The problem: My team&#x27;s secrets lived in a mix of Slack DMs, shared Google Docs, and .env files that definitely weren&#x27;t in .gitignore at some point. Enterprise tools like Vault required more DevOps time than we had. Doppler was close but felt heavier than we needed. What KeyEnv does: keyenv init # link project keyenv pull # sync secrets to local .env keyenv run -- npm start # inject secrets, run command That&#x27;s basically it. Secrets are encrypted client-side (AES-256-GCM) before leaving your machine. Zero-knowledge architecture—we can&#x27;t read your secrets even if we wanted to. Technical details: - Single Rust binary, no runtime dependencies - Works offline (cached secrets) - RBAC for teams (owner&#x2F;admin&#x2F;member&#x2F;viewer) - Service tokens for CI&#x2F;CD - Full audit trail Honest tradeoffs: - SaaS only, no self-hosted option - Fewer integrations than Doppler - If you need dynamic secrets or PKI, use Vault Pricing: Free tier (3 projects, 100 secrets), $12&#x2F;user&#x2F;month for teams. Would love feedback on the CLI UX and any rough edges. Happy to answer questions about the architecture. </code></pre> <a href="https:&#x2F;&#x2F;www.keyenv.dev" rel="nofollow">https:&#x2F;&#x2F;www.keyenv.dev</a>

Show HN: WebTerm – Browser-based terminal emulator

Show HN: WebTerm – Browser-based terminal emulator

Show HN: WebGPU React Renderer Using Vello

Show HN: WebGPU React Renderer Using Vello I&#x27;ve built a package to use Raph Levien&#x27;s Vello as a blazing fast 2D renderer for React on WebGPU. It uses WASM to hook into the Rust code

Show HN: On the edge of Apple Silicon memory speeds

Show HN: On the edge of Apple Silicon memory speeds I have developed open source CLI-tool for Apple Silicon macOS. It measures memory speeds in different ways and also latency. It can achieve up to 96-97% efficiency on read speed on M4 base what is advertised as 120GB&#x2F;s. All memory operations are in assembly.<p>I would really appreciate for results on different CPU&#x27;s how benchmark works on those. I have been able to test this on M1 and M4.<p>command : &#x27;memory_benchmark -non-cacheable -count 5 -output results.JSON&#x27; (close all applications before running)<p>This will generate JSON file where you find sections copy_gb_s, read_gb_s and write_gb_s statics.<p>Example M4 with 10 loops: &quot;copy_gb_s&quot;: { &quot;statistics&quot;: { &quot;average&quot;: 106.65421233311835, &quot;max&quot;: 106.70240696071005, &quot;median&quot;: 106.65069297260811, &quot;min&quot;: 106.6336774994254, &quot;p90&quot;: 106.66606919223108, &quot;p95&quot;: 106.68423807647056, &quot;p99&quot;: 106.69877318386216, &quot;stddev&quot;: 0.01930653530818627 }, &quot;values&quot;: [ 106.70240696071005, 106.66203166240008, 106.64410802226159, 106.65831409449595, 106.64148106986977, 106.6482935780762, 106.63974821679058, 106.65896986001393, 106.6336774994254, 106.65309236714002 ] }, &quot;read_gb_s&quot;: { &quot;statistics&quot;: { &quot;average&quot;: 115.83111228356601, &quot;max&quot;: 116.11098114619033, &quot;median&quot;: 115.84480882265643, &quot;min&quot;: 115.56959026587722, &quot;p90&quot;: 115.99667266786554, &quot;p95&quot;: 116.05382690702793, &quot;p99&quot;: 116.09955029835784, &quot;stddev&quot;: 0.1768243167963439 }, &quot;values&quot;: [ 115.79154681380165, 115.56959026587722, 115.60574235736468, 115.72112860271632, 115.72147129262802, 115.89807083151123, 115.95527337086908, 115.95334642887214, 115.98397172582945, 116.11098114619033 ] }, &quot;write_gb_s&quot;: { &quot;statistics&quot;: { &quot;average&quot;: 65.55966046805113, &quot;max&quot;: 65.59040040480241, &quot;median&quot;: 65.55933583741347, &quot;min&quot;: 65.50911885624045, &quot;p90&quot;: 65.5840272860955, &quot;p95&quot;: 65.58721384544896, &quot;p99&quot;: 65.58976309293172, &quot;stddev&quot;: 0.02388146120866979 },<p>Patterns benchmark also shows bit more of memory speeds. command: &#x27;memory_benchmark -patterns -non-cacheable -count 5 -output patterns.JSON&#x27;<p>Example M4 from 100 loops: &quot;sequential_forward&quot;: { &quot;bandwidth&quot;: { &quot;read_gb_s&quot;: { &quot;statistics&quot;: { &quot;average&quot;: 116.38363691482549, &quot;max&quot;: 116.61212708384109, &quot;median&quot;: 116.41264548721367, &quot;min&quot;: 115.449510036971, &quot;p90&quot;: 116.54143114134801, &quot;p95&quot;: 116.57314206456576, &quot;p99&quot;: 116.60095068065866, &quot;stddev&quot;: 0.17026641589059727 } } } }<p>&quot;strided_4096&quot;: { &quot;bandwidth&quot;: { &quot;read_gb_s&quot;: { &quot;statistics&quot;: { &quot;average&quot;: 26.460392735220456, &quot;max&quot;: 27.7722419653915, &quot;median&quot;: 26.457051473208285, &quot;min&quot;: 25.519925729459107, &quot;p90&quot;: 27.105171215736604, &quot;p95&quot;: 27.190715938337473, &quot;p99&quot;: 27.360449534513144, &quot;stddev&quot;: 0.4730857335572576 } } } }<p>&quot;random&quot;: { &quot;bandwidth&quot;: { &quot;read_gb_s&quot;: { &quot;statistics&quot;: { &quot;average&quot;: 26.71367836895143, &quot;max&quot;: 26.966820487564327, &quot;median&quot;: 26.69907406197067, &quot;min&quot;: 26.49374804466308, &quot;p90&quot;: 26.845236287807374, &quot;p95&quot;: 26.882004355057887, &quot;p99&quot;: 26.95742242818151, &quot;stddev&quot;: 0.09600564296001704 } } } }<p>Thank you for reading :)

No other tools from this source yet.