mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-11 08:06:13 +00:00
Remove DynamoDB-backed storage paths, add Postgres/HTTP repositories for mailbox state maps, wire the internal message-map API, and add provisioning runner/migration plumbing.
54 lines
2.3 KiB
Go
54 lines
2.3 KiB
Go
// Package encryptedkeys stores per-user envelope-encrypted DEKs.
|
|
//
|
|
// A DEK (data encryption key) is a 32-byte AES-256 key generated per user by
|
|
// the kms.Provider. The KMS encrypts it; the encrypted form ("blob") is what
|
|
// this package stores. Workers and backend services never store the plaintext
|
|
// DEK on disk; they fetch the blob, ask KMS to decrypt it, and hold the
|
|
// plaintext in a short-TTL Redis cache.
|
|
//
|
|
// Durability matters absolutely. Losing an encrypted DEK is unrecoverable —
|
|
// every encrypted mailbox credential, OAuth refresh token, and stored message
|
|
// for that user becomes permanently unreadable. Implementations must therefore
|
|
// be backed by storage with strong durability guarantees (Postgres or
|
|
// equivalent). NATS JetStream KV is intentionally not offered here.
|
|
//
|
|
// Workers MUST NOT connect to Postgres directly (per CLAUDE.md), so the worker
|
|
// process uses the HTTP implementation, which calls a backend endpoint that
|
|
// in turn talks to the chosen durable store.
|
|
package encryptedkeys
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ErrAlreadyExists is returned by Put when a DEK is already stored for the
|
|
// given user. Overwriting would silently invalidate every prior ciphertext;
|
|
// rotation must use a separate explicit code path that re-encrypts existing
|
|
// data under the new DEK.
|
|
var ErrAlreadyExists = errors.New("encryptedkeys: dek already exists for user")
|
|
|
|
// Store is the abstraction over encrypted-DEK durable storage.
|
|
type Store interface {
|
|
// Put inserts the encrypted DEK. Returns ErrAlreadyExists if a DEK is
|
|
// already stored for the user.
|
|
Put(ctx context.Context, userID uuid.UUID, encryptedDEKB64 string) error
|
|
|
|
// Get returns the encrypted DEK as base64, or the empty string if no DEK
|
|
// is stored for the user. (Empty string, not error, matches today's
|
|
// repository contract and lets the cipher service distinguish "never had
|
|
// one — generate now" from "lookup failed — bail out".)
|
|
Get(ctx context.Context, userID uuid.UUID) (string, error)
|
|
|
|
// Delete removes the stored DEK. Idempotent: deleting a missing key is
|
|
// not an error. Use with extreme caution — see package docs on
|
|
// unrecoverability.
|
|
Delete(ctx context.Context, userID uuid.UUID) error
|
|
|
|
// Name returns a short identifier ("postgres", "http") for admin UI
|
|
// display and audit logs.
|
|
Name() string
|
|
}
|