Skip to content

feat(secrets): encrypt provider, gateway, and mcp secrets at rest - #77

Merged
devproje merged 1 commit into
masterfrom
feat/encrypt-provider-secrets
Sep 23, 2026
Merged

devproje merged 1 commit into
masterfrom
feat/encrypt-provider-secrets

Conversation

@devproje

@devproje devproje commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

What this changes

Provider API keys (providers.api_key), gateways.json endpoint keys, and mcp.json server auth headers were all stored as plaintext, protected only by 0600 file permissions or the local sqlite file. maskSecret hid them on display but never touched what was persisted. This adds AES-256-GCM encryption at rest for all three, keyed by a new secret.key file kept separate from mininaru.key (which authenticates the daemon itself and is intentionally out of scope). Existing plaintext values are decrypted transparently on read and re-encrypted the next time they're saved, so no separate migration step is needed.

How it was verified

  • make test-race passes
  • New behaviour has a test, or there is nothing to test

Added util/crypto_test.go covering encrypt/decrypt round-trip, empty-string passthrough, and legacy-plaintext passthrough through Decrypt. Also manually confirmed a provider's stored api_key is enc:v1:... ciphertext in the sqlite file and round-trips back to the original value through ProviderCreate/ProviderRead.

Checklist

  • Follows docs/CONVENTION.md: no comments, no :=, one var block per function in first-use order with err last, callees before callers, and main unconditionally last
  • New .go files carry the two-line SPDX header
  • Documentation updated if behaviour a user can see has changed (none needed — no user-visible behaviour changed)
  • My contribution is licensed GPL-3.0-only, matching the project

Provider API keys sat as plaintext in the providers.api_key column,
gateways.json stored remote endpoint keys unencrypted, and mcp.json kept
MCP server auth headers in the clear. All three were only protected by
0600 file permissions or the local sqlite file itself; the existing
maskSecret helper hid values on display but never touched what was
persisted.

Add util/crypto.go with AES-256-GCM Encrypt/Decrypt helpers keyed by a
new secret.key (generated the same way as mininaru.key, but kept
separate from it since that file authenticates the daemon rather than
protecting stored credentials). Decrypt passes unprefixed values
through unchanged, so existing plaintext rows and config entries
migrate in place the next time they're written rather than needing a
separate migration step.
@devproje
devproje merged commit f559f94 into master Sep 23, 2026
3 checks passed
@devproje
devproje deleted the feat/encrypt-provider-secrets branch September 23, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant