v0.53.0: Phase 2 capture side — per-app secret-free recovery unit
Each app's on-drive backup becomes a self-contained, recreatable recovery unit: compose/ (docker-compose.yml + .felhom.yml + secret-stripped app.yaml) alongside the existing db-dumps/ + volume-dumps/, plus a secret-free manifest.json (image pins, secret env-var NAMES, data_key names, checksums). The unit stores no secret value, no data-key, and not the image — secrets are recovered at restore from the guest's own app.yaml (live/PBS), never regenerated. - appbackup: RecoveryUnit* path helpers, RecoveryInfo + GetStackRecoveryInfo, ParseComposeImages; AppDBDump/Volume refactored onto RecoveryUnitPath. - backup: recovery_unit.go (manifest + CaptureRecoveryUnit), wired into RunDBDumps; capture test proves secret-free. - stacks: DeployField.DataKey + Metadata.DataKeyEnvVars(); main.go stackAdapter implements GetStackRecoveryInfo (excludes secret-named + encrypted values). - Restore-from-unit recreate + fail-closed gate + live AdventureLog validation: next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,23 @@ type DeployField struct {
|
||||
Description string `yaml:"description" json:"description"`
|
||||
LockedAfterDeploy bool `yaml:"locked_after_deploy" json:"locked_after_deploy"`
|
||||
Options []SelectOption `yaml:"options" json:"options,omitempty"`
|
||||
// DataKey marks a field as a DATA-ENCRYPTING key (e.g. AdventureLog's "Titkosítási kulcs"):
|
||||
// the app encrypts stored data with it, so regenerating it would render restored data
|
||||
// unreadable. It is a fail-closed annotation only — the recovery unit never stores secrets;
|
||||
// at restore the controller refuses (rather than silently restoring garbage) if a data_key
|
||||
// app's key cannot be recovered from the guest's app.yaml (live or via PBS). See Phase 2.
|
||||
DataKey bool `yaml:"data_key,omitempty" json:"data_key,omitempty"`
|
||||
}
|
||||
|
||||
// DataKeyEnvVars returns the env-var names of fields marked data_key:true.
|
||||
func (m *Metadata) DataKeyEnvVars() []string {
|
||||
var out []string
|
||||
for _, f := range m.DeployFields {
|
||||
if f.DataKey {
|
||||
out = append(out, f.EnvVar)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// SelectOption is a choice for "select" type fields.
|
||||
|
||||
Reference in New Issue
Block a user