kept data: the choice at reinstall, the list, the read-only view, the load (09 decision 36); R-690 fixed
gates / gates (push) Successful in 26s

An install over an app's kept drive folder (appdata/<app> non-empty) asks the household:
"use my kept data" (a load from the newest copy of THIS drive's install, own unit or
second-drive mirror, then the template's after_load) or "start fresh" (the folder is
renamed into <drive>/kept/<app>/<date>/ with the removed app's unit; nothing deleted).
The install API answers 409 kept_data_choice until one is chosen; DeployStack refuses
too. New page Megorzott adatok / Kept data (/kept-data): Load / Look / Delete (typed
confirmation, the only deletion of kept data). FileBrowser gets a read-only source.
The drive-full warning names the kept folders. <drive>/kept is protected and outside
every backup leg.

R-690: the removed-app restore (R-487) never found a unit on a DATA drive — it asked
GetStackComposePath (true for every catalog app) and restored nextcloud with no env.
Now isStackDeployed; pinned with a production-shaped provider.

Red-proofs: audits/night-2026-09-26/E/redproofs/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-25 13:32:30 +02:00
parent 5a731b45b9
commit 43e99d160c
44 changed files with 3311 additions and 37 deletions
+13 -1
View File
@@ -48,7 +48,12 @@ type Metadata struct {
AppInfo AppInfo `yaml:"app_info" json:"app_info"`
OptionalConfig []OptionalConfigGroup `yaml:"optional_config" json:"optional_config"`
HealthCheck *HealthCheckConfig `yaml:"healthcheck,omitempty" json:"healthcheck,omitempty"`
Integrations []IntegrationDef `yaml:"integrations,omitempty" json:"integrations,omitempty"`
// AfterLoad (`09` §3 decision 36, E1 2026-09-25) is ONE command the box runs once after it loads an
// app's kept data (a database from a backup under files kept on the drive) — for an app whose own
// index of its files must be rebuilt. Measured on nextcloud: a file written after the backup is on
// the drive and invisible until `occ files:scan --all`. Optional; absent = nothing runs.
AfterLoad *AfterLoadCommand `yaml:"after_load,omitempty" json:"after_load,omitempty"`
Integrations []IntegrationDef `yaml:"integrations,omitempty" json:"integrations,omitempty"`
// InitialCreds: for apps that auto-generate a first-login credential into a file inside the
// container (e.g. Crafty's default-creds.txt). The controller reads + parses that file live and
// surfaces it on the app page, so the customer never has to dig through logs. Optional.
@@ -533,3 +538,10 @@ func (m *Metadata) HasOptionalConfig() bool {
func (m *Metadata) HasIntegrations() bool {
return len(m.Integrations) > 0
}
// AfterLoadCommand is `.felhom.yml`'s `after_load:` — run with `docker compose exec -T` in Service.
type AfterLoadCommand struct {
Service string `yaml:"service" json:"service"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
Command []string `yaml:"command" json:"command"`
}