Skip to Content

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.