Skip to content

Store credentials as YAML with optional encryption at rest - #115

Open
P6g9YHK6 wants to merge 2 commits into
leonboe1:mainfrom
P6g9YHK6:upstream-yaml-secrets-encryption
Open

Store credentials as YAML with optional encryption at rest#115
P6g9YHK6 wants to merge 2 commits into
leonboe1:mainfrom
P6g9YHK6:upstream-yaml-secrets-encryption

Conversation

@P6g9YHK6

@P6g9YHK6 P6g9YHK6 commented Aug 11, 2026

Copy link
Copy Markdown

Two commits:

Store credentials as YAML instead of JSON
Auth/secrets.json becomes Auth/auth.yaml. An existing secrets.json is migrated automatically the first time it's read - written straight back out as auth.yaml, with the old file left in place untouched (as a backup, and so a downgrade isn't a hard break). Every load after that first migration hits the YAML file directly and never looks at the JSON file again.

No behavior change for callers - get_cached_value/set_cached_value/get_cached_value_or_set keep the same signatures.

Add optional encryption at rest via SECRETS_ENCRYPTION_KEY
Every value written to auth.yaml (OAuth tokens, FCM credentials, vault keys, ...) is encrypted with AES-256-GCM when this env var is set - any string works, it's hashed down to an AES-256 key. Unset keeps the previous plain-text behavior and prints a one-time warning saying so.

Values are JSON-serialized before encrypting so this isn't limited to flat strings (fcm_credentials is a nested dict), and prefixed with a versioned marker so an encrypted value is distinguishable from a plain one - anything without the prefix is read back as-is, so values written before a key was ever set keep working with no migration step. A wrong or rotated key makes decrypt fail, which is treated as a missing value (None) rather than crashing every caller of get_cached_value.

The legacy secrets.json migration also encrypts each value on the way in when a key is already set.

No new hard dependency beyond pyyaml - cryptography (used for the AES-GCM encryption) was already a requirement.

Auth/secrets.json becomes Auth/auth.yaml. An existing secrets.json is
migrated automatically the first time it's read - written straight
back out as auth.yaml, with the old file left in place untouched (as
a backup, and so a downgrade isn't a hard break). Every load after
that first migration hits the YAML file directly and never looks at
the JSON file again.

No behavior change for callers - get_cached_value/set_cached_value/
get_cached_value_or_set keep the same signatures.
Every value written to auth.yaml (OAuth tokens, FCM credentials,
vault keys, ...) is encrypted with AES-256-GCM when this env var is
set - any string works, it's hashed down to an AES-256 key. Unset
keeps the previous plain-text behavior and prints a one-time warning
saying so.

Values are JSON-serialized before encrypting so this isn't limited to
flat strings (fcm_credentials is a nested dict), and prefixed with a
versioned marker so an encrypted value is distinguishable from a
plain one - anything without the prefix is read back as-is, so values
written before a key was ever set keep working with no migration
step. A wrong or rotated key makes decrypt fail with InvalidTag,
which is treated as a missing value rather than crashing every
caller of get_cached_value.

The legacy secrets.json migration also encrypts each value on the
way in when a key is already set.
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