Add schema-version consistency to persisted formats that lacked it - #238
Merged
Conversation
foxly-it
force-pushed
the
feat/schema-version-consistency
branch
from
August 13, 2026 16:45
a20aeaf to
fcb8d42
Compare
) Scoped deliberately to schema-version + fail-closed consistency rather than a full transform-function migration framework, per explicit user direction - RootGuard doesn't yet have a real breaking-schema-change history that would justify one. Every persisted JSON file across rootguard-core/rootguard-updater was audited. Most already had adequate cheap validity checks and low-consequence failure modes on a shape mismatch; adding versioning there would have been ceremony. Two genuine gaps: - updater/backups.go's per-backup manifest.json - the direct sibling of backupexport.Manifest, which already has SchemaVersion, but authorizes restoring files into a live container during rollback without one. Now checked before both retention-scan trust and rollback restore. - unbound/settings.go's settings.json - the most complex, most frequently evolving persisted format, with an existing hand-rolled additive-field migration story (jsonFieldExists) a naive "reject on any mismatch" would have conflicted with. Gets a schema_version envelope kept deliberately off the Settings type itself (also the guided-settings HTTP API response shape - a persistence concern shouldn't leak into that). Load refuses only a *newer* version than the build knows; an absent or older version still flows through the existing additive defaulting unchanged, and its existing test (TestLoadMigratesGuidedControls) passes unmodified.
foxly-it
force-pushed
the
feat/schema-version-consistency
branch
from
August 13, 2026 17:11
fcb8d42 to
7dd5f9b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #237.
Summary
Scoped deliberately, per explicit user direction, to schema-version + fail-closed consistency rather than a full transform-function migration framework - RootGuard doesn't yet have a real breaking-schema-change history that would justify one.
Every persisted JSON file across
rootguard-core/rootguard-updaterwas audited (status.json ×3, images.json, backup-settings.json, sessions.json, credentials.json, audit.json, and both already-versioned formats). Most already have adequate cheap validity checks and low-consequence failure modes on a shape mismatch (re-run a step, lose a log entry) - adding versioning there would be ceremony, not a real improvement. Two genuine gaps closed:updater/backups.go's per-backupmanifest.json- the direct sibling ofbackupexport.Manifest(which already hasSchemaVersion), but authorizes restoring files into a live container during rollback without one. Now checked (backupManifestSchemaVersion) before both retention-scan trust (validBackupManifest) and rollback restore (verifyBackupManifest).unbound/settings.go'ssettings.json- the most complex, most frequently evolving persisted format, with an existing hand-rolled additive-field migration story (jsonFieldExists) that a naive "reject on any mismatch" would have conflicted with. Gets aschema_versionenvelope (persistedSettings) kept deliberately off theSettingstype itself - which is also the guided-settings HTTP API response shape, so a persistence concern shouldn't leak into it.Loadrefuses only a newer version than the build knows (the genuinely dangerous case: fields repurposed with a different meaning); an absent or older version still flows through the existing additive-field defaulting unchanged.Test plan
go build ./...,go vet ./...,go test ./...(rootguard-core)TestLoadMigratesGuidedControls(the pre-existing additive-migration regression test) passes unmodified against a genuinely old, pre-versioning fixtureTestVerifyBackupManifestRejectsUnsupportedSchemaVersion,TestLoadReadsSchemaVersionedSettings(round-trip),TestLoadRefusesNewerSchemaVersion