feat(store): envelope DEK with admin/agent wrap slots

Open() now opens LOCKED; InitKeys generates a DEK sealed under both KEKs;
Unlock loads it from the role's slot (admin slot has no agent fallback).
s.key becomes the DEK, so account/mail crypto is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 22:52:21 +01:00
parent c52f30898b
commit cb0425f18d
4 changed files with 224 additions and 14 deletions
+4 -2
View File
@@ -19,7 +19,9 @@ type Store struct {
}
// Open opens (creating if needed) the DB at path and applies the schema.
func Open(path string, key []byte) (*Store, error) {
// The store opens LOCKED: call InitKeys (first run) or Unlock before any
// secret read/write.
func Open(path string) (*Store, error) {
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
return nil, fmt.Errorf("create db dir: %w", err)
}
@@ -39,7 +41,7 @@ func Open(path string, key []byte) (*Store, error) {
db.Close()
return nil, fmt.Errorf("apply schema: %w", err)
}
s := &Store{db: db, key: key}
s := &Store{db: db}
if _, err := s.GetSetting("schema_version"); err != nil {
if err := s.SetSetting("schema_version", strconv.Itoa(schemaVersion)); err != nil {
db.Close()