Documentation
¶
Index ¶
- Constants
- Variables
- func AAD(table string, orgID, rowID uuid.UUID) []byte
- func DecodeB64(s string) ([]byte, error)
- func DecryptAESGCM(cipherNoTag, key, iv, tag []byte) ([]byte, error)
- func DecryptAESGCMWithAAD(cipherNoTag, key, iv, tag, aad []byte) ([]byte, error)
- func DeriveKey(master []byte, purpose KeyPurpose) ([]byte, error)
- func DeriveKeyFromBase64(encoded string, purpose KeyPurpose) ([]byte, error)
- func EncodeB64(b []byte) string
- func EncryptAESGCM(plaintext, key []byte) (cipherNoTag, iv, tag []byte, _ error)
- func EncryptAESGCMWithAAD(plaintext, key, aad []byte) (cipherNoTag, iv, tag []byte, _ error)
- func MasterKeyFromBase64(encoded string) ([]byte, error)
- func RandomBytes(n int) ([]byte, error)
- func TenantDatabasePrefix(prefix string) string
- type KeyPurpose
- type Sealed
Constants ¶
const DefaultTenantDatabasePrefix = "tenant"
DefaultTenantDatabasePrefix names tenant databases when DATABASE_PREFIX is unset. It is what every tenant was called before the prefix was configurable, so leaving the setting out changes nothing.
const MasterKeyLen = 32
Variables ¶
var ErrNotSealed = errors.New("value is not sealed")
var LegacyKeyID = uuid.NewSHA1(uuid.NameSpaceOID, []byte("apiary/encryption/legacy"))
LegacyKeyID identifies the single ENCRYPTION_MASTER_KEY, for deployments that have not configured a key registry.
.env describes the intended end state: ENCRYPTION_KEYS holds several key-encryption keys as id:base64 pairs, "and leaving both blank keeps using the single master key above, which is read as the key with id legacy". Until that registry exists, sealing still needs a stable identifier — utils.Seal refuses uuid.Nil precisely so that a rotation can tell re-wrapped rows from un-rewrapped ones, and a zero id would make rotation unverifiable.
Derived rather than generated so it is identical in every process and across restarts; a random id per boot would tag rows with an identifier that means nothing afterwards.
Functions ¶
func DecryptAESGCM ¶
func DecryptAESGCMWithAAD ¶
func DeriveKeyFromBase64 ¶
func DeriveKeyFromBase64(encoded string, purpose KeyPurpose) ([]byte, error)
func EncryptAESGCM ¶
func EncryptAESGCMWithAAD ¶
func MasterKeyFromBase64 ¶
func RandomBytes ¶
func TenantDatabasePrefix ¶ added in v0.1.5
TenantDatabasePrefix normalizes a configured DATABASE_PREFIX.
The one place it happens, so the worker's startup validation checks exactly the value tenants are then named from. A trailing underscore is dropped because the separator is added by the caller: both "apiary" and "apiary_" name databases apiary_<uuid>, never apiary__<uuid>.
Types ¶
type KeyPurpose ¶
type KeyPurpose string
const ( // These are the `info` parameter of the HKDF that derives every key in the // system, so the STRING IS PART OF THE KEY. They are not labels. // // The /v2 suffix records that they were renamed from an "eyrie/" prefix on // 2026-08-23, which re-derived every key. Anything sealed under the v1 // derivation is unreadable to this build: // // TenantDEK sealed credentials in tenant databases // TenantDSN tenant connection strings — the databases become unreachable // TOTPSeed enrolled authenticators // // Nothing errors when this changes; the data simply stops opening. The // version bump exists so the break is legible rather than mysterious. // // CHANGING ONE AGAIN IS A KEY-ROTATION EVENT, not an edit: every affected row // must be decrypted under the old purpose and re-sealed under the new one, // with dual-read during rollout, BEFORE the constant moves. A global // find/replace reaches these — that is exactly how the 2026-08-23 change // happened, and it was caught only by // TestUCRDeriveKeyIsPinnedToItsExactConstruction, which pins derived bytes // rather than strings. KeyPurposeTOTPSeed KeyPurpose = "apiary/mfa/totp-seed/v2" KeyPurposeTenantDEK KeyPurpose = "apiary/tenant/dek/v2" KeyPurposeTenantDSN KeyPurpose = "apiary/tenant/dsn/v2" // KeyPurposeQueuePayload seals the body of a queued email. // // The prefix differs from the three above, and that is deliberate rather than // an oversight. These strings are HKDF info parameters: changing one changes // every key derived under it, so the three above are frozen — renaming them // would make every sealed credential, every tenant DSN and every TOTP seed // permanently unreadable. Nothing has ever been sealed under this new purpose, // so it is free to carry the correct name, and new purposes should. KeyPurposeQueuePayload KeyPurpose = "apiary/queue/payload/v1" )