secrets

package
v0.5.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package secrets encrypts sensitive values (e.g. connection passwords) at rest with AES-256-GCM under a configured key. See ADR 0019.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoKey = errors.New("no encryption key configured")

ErrNoKey reports that no encryption key is configured; callers must treat secret writes as unavailable rather than storing plaintext.

Functions

func LooksLikeHalfEntropyHexKey

func LooksLikeHalfEntropyHexKey(s string) bool

LooksLikeHalfEntropyHexKey reports whether a key is 32 characters of pure hexadecimal — the signature of `openssl rand -hex 16`, which the project's own docs recommended until they were corrected.

ParseKey accepts a 32-character string as 32 RAW bytes, so such a key is used as an AES-256 key carrying 128 bits of entropy. The cipher is still AES-256; the secret behind it is half the size intended, and nothing about the configuration says so.

This is a warning and not a rejection because the two shapes cannot be told apart with certainty: a legitimate 32-character passphrase made only of the characters 0-9a-f is unlikely but perfectly valid, and refusing it would break an operator who did nothing wrong. Reporting it lets the caller say something while still starting.

func ParseKey

func ParseKey(s string) ([]byte, error)

ParseKey decodes a configured key string into 32 raw bytes, accepting a raw 32-char string, 64-char hex, or base64 (standard or raw).

func ParseKeys

func ParseKeys(s string) ([][]byte, error)

ParseKeys parses a comma-separated key list. The FIRST key encrypts and decrypts; every later one only decrypts.

This is the shape Apache Airflow's `fernet_key` uses, and Dexaflow is an Airflow-compatible control plane, so an operator arriving from Airflow already knows the rule: put the new key first, the old ones after, then run the re-encryption and drop the tail.

A single key parses to exactly what ParseKey returns for it, so every deployment that predates the list keeps working unchanged.

A malformed entry is an error rather than a silent drop. Dropping it would leave an operator believing a predecessor is in the read set when it is not, and the next thing they do is delete the only remaining copy of it.

Types

type Cipher

type Cipher interface {
	Encrypt(plaintext string) (string, error)
	Decrypt(ciphertext string) (string, error)
}

Cipher encrypts and decrypts secret values.

func NewAESGCM

func NewAESGCM(key []byte) (Cipher, error)

NewAESGCM builds a Cipher from a 32-byte key. The key may be given raw (32 bytes), hex (64 chars), or base64 (standard or raw); ParseKey handles the decoding.

type StaleReader

type StaleReader interface {
	Cipher
	// DecryptStale decrypts and reports whether the value was read with a
	// fallback key rather than the primary one.
	DecryptStale(ciphertext string) (plaintext string, stale bool, err error)
}

StaleReader is a Cipher that can also say whether a value it just decrypted was written under an older key, so a caller can re-encrypt it.

func WithFallback

func WithFallback(primary Cipher, fallbacks ...Cipher) StaleReader

WithFallback returns a Cipher that writes with primary and reads with primary then fallbacks, skipping nil entries.

It always wraps, including when no fallback survives: the result is a StaleReader either way, which callers rely on. With an empty fallback list every read reports fresh, so a single-key deployment behaves exactly as the bare primary does.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL