Restore architecture and maintenance contract
This document describes the checked-out architecture of the generic PMIVerse
restore subsystem. It is a maintenance reference, not an operational approval.
The authoritative public command remains
/srv/pmiverse/_pmiconfig/scripts/restore/restore_database.sh.
Entry point and library loading
restore_database.sh resolves its own canonical path with readlink -f. Direct,
cross-working-directory, and symlink invocations therefore load the libraries
from the canonical adjacent scripts/restore/lib/ directory. The command checks
that each required library is a readable regular file and exits nonzero with a
library-specific error when one is unavailable or cannot be sourced.
The scoped scripts/restore/.shellcheckrc enables source analysis. Entry-point
source directives identify the adjacent libraries for ShellCheck and IDE
inspections, including shared state read by publication and rollback functions.
The five libraries are sourced in-process. Sourcing defines functions only: it
is silent, performs no restore operation, acquires no lock, opens no persistent
descriptor, and installs no trap. The libraries are non-executable mode 0644;
the public command and its focused shell tests are executable mode 0755.
Responsibility map
scripts/restore/lib/restore_config.bash
Owns configuration initialization, help and argument parsing, explicit fixture configuration loading for direct library tests, legacy per-target and registry resolution, suite-layout derivation, input validation, and dry-run rendering. Legacy commands also use its isolated source-path resolver, honoring registry entries only when their suite version matches and preserving caller state. It does not select or inspect an archive and does not perform a restore.
scripts/restore/lib/restore_archive.bash
Owns exact or newest archive/source selection, path-component and ZIP-entry safety, extracted-tree symlink inspection, single-dump discovery and validation, and prioritized filestore discovery. It supports plain SQL and PostgreSQL custom dump validation but does not publish either restored resource.
scripts/restore/lib/restore_postgresql.bash
Owns low-level PostgreSQL database-existence and exact-state observation,
role/ownership publication preflight, connection termination, temporary database
creation, plain SQL restore, custom *.dump/*.backup restore, identifier-safe
rename, and exact drop operations. It supplies primitives; it does not decide the
publication or rollback sequence.
scripts/restore/lib/restore_neutralization.bash
Owns SQL neutralization and the embedded Odoo-shell post-processing program.
That work covers outgoing and incoming mail protection, cron and installed
integration disabling, supported bank-sync disconnection, IAP cleanup, and
website.robots. Optional models are handled explicitly. Required ORM failures
are collected across attempted operations and raised as one aggregate failure.
scripts/restore/lib/restore_publication.bash
Owns publication-state initialization and observation, database-before-filestore publication sequencing, rollback classification and execution, prepublication recovery, recovery diagnostics, and the exact ownership/state predicates used within that coordination. It establishes database rollback before attempting filesystem rollback and preserves artifacts when state is ambiguous.
scripts/restore/restore_database.sh
The public orchestrator owns canonical library loading and overall phase
sequencing. It retains resource-lock identity, PostgreSQL-before-filestore lock
ordering, dynamic lock descriptors, acquisition and release; trap and signal
ownership; worktree and staged-artifact cleanup ownership; archive,
configuration, restore, neutralization, publication, and final-cleanup ordering;
neutralization invocation policy; the overall success boundary; and public
status, stdout, stderr, and exit mapping. SIGINT maps to 130 and SIGTERM to
143 through orchestrator-installed traps.
Safety and ownership model
Normal development validation is mocked-boundary validation. There is no
expectation of a live PostgreSQL, Odoo, deployment, restore, neutralization, or
target-filesystem run. Tests must put fake operational executables first in
PATH, use synthetic Odoo modules where needed, and confine mutable artifacts
to disposable fixture roots.
SQL values and PostgreSQL identifiers remain data, not shell syntax. Database
names are passed as command arguments or through psql -v; database rename uses
PostgreSQL format(... %I ...) identifier quoting. Shell eval, constructed
shell command strings, and direct unsafe SQL interpolation are prohibited.
Before extraction, archive paths reject symlinked or unsafe source components, and ZIP entries reject absolute paths, traversal components, backslashes, control characters, and symlink entries. The extracted tree is inspected again for symlinks. A restore proceeds only with one supported, readable, non-empty dump and an unambiguous supported filestore discovery result.
Publication always renames the database before publishing a filestore. On failure, rollback establishes the database result before changing filesystem state. Rename, drop, move, and recursive removal decisions depend on exact database-presence and direct-child path predicates. Ambiguous observation, unexpected ownership, or an unsafe path causes refusal and preservation of temporary, staged, and rollback artifacts with recovery diagnostics.
The orchestrator owns traps, signals, cleanup, and lock lifetime. Preparation cleanup applies before publication state exists; publication and post-restore failures delegate reconciliation to the publication library; cleanup-phase failures preserve rollback artifacts and print recovery guidance. Locks remain held through the success boundary or failure handling and are released by closing their dynamic descriptors.
Outgoing and incoming mail protections are required during Odoo post-processing. Requested neutralization adds the required cron, integration, and robots work. The embedded program attempts all relevant operations and reports aggregate required failures rather than stopping after the first one.
Database and filestore publication is coordinated rollback, not cross-system atomicity. Individual PostgreSQL and same-filesystem rename operations have their own guarantees, but the pair is not one transaction. Recovery therefore depends on observed exact state and artifact preservation, not an atomicity claim.
Configuration, compatibility, and source paths
Active configuration defaults to
/srv/pmiverse/_pmiregistry/config/pmiverse.conf. The PMIVERSE_CONF
environment variable selects an alternative trusted Bash configuration. The
repository's config/pmiverse.conf is a generic template and is never loaded
implicitly. Direct library tests pass synthetic fixture paths; command tests
select fixtures with PMIVERSE_CONF.
Configuration schema, precedence, derivation, and registry behavior remain
documented in the generic restore runbook.
The public restore entry point remains
/srv/pmiverse/_pmiconfig/scripts/restore/restore_database.sh. Supported ZIP
layouts and plain SQL, *.dump, and *.backup behavior remain unchanged.
The legacy commands in _pmiregistry/scripts/restore/ are
separate implementations, not wrappers around the
generic command; see
the legacy command reference.
The synchronized-laptop implementation is
scripts/laptop-backup/restore_latest_backup.sh. The five adjacent
scripts/restore/lib/*.bash files are internal sourced libraries extracted from
the unchanged generic public entry point; they are not new public commands.
Focused testing map
| Change area | Owning focused test |
|---|---|
| CLI parsing, configuration, derivation, dry-run | tests/test_restore_config_library.sh and tests/test_restore_cli_contract.sh |
| Local mirror paths, legacy source overrides and version fallback | _pmiregistry/tests/test_restore_local_sources.sh |
| Archive selection, safety, dump and filestore discovery | tests/test_restore_archive_library.sh |
| PostgreSQL observation and command primitives | tests/test_restore_postgresql_library.sh |
| SQL and Odoo neutralization, optional models, aggregate failures | tests/test_restore_neutralization_library.sh |
| Publication, rollback, recovery, exact state classification | tests/test_restore_publication_library.sh |
| Integrated locks, traps, signals, cleanup, ordering, and success boundary | tests/test_restore_safety.sh |
| Architecture, modes, active paths, legacy classification, completion | tests/test_restore_docs_completion.sh |
After a focused library change, run its owning test plus
tests/test_restore_cli_contract.sh when public loading or CLI behavior may be
affected, and tests/test_restore_safety.sh when orchestration, state, cleanup,
or ordering may be affected. Documentation or layout changes run
tests/test_restore_docs_completion.sh and any navigation test covering the
changed path.
These tests must not reach real PostgreSQL, Odoo, deployment, restore, or target filesystems. Operationally capable commands are exercised only behind inspected fake executables and disposable fixture roots.
Maintenance constraints
Keep restore_database.sh as the stable public command and keep the libraries
adjacent and source-only. New extraction is justified by coherent ownership,
not line count. Preserve configuration precedence, archive formats, lock
identity and ordering, database-before-filestore publication, database-first
rollback, exact ownership checks, signal statuses, neutralization requirements,
and public output/status contracts. Any change to these behaviors requires
focused characterization plus the integrated safety suite; documentation alone
must not redefine runtime behavior.