|
|
|
@@ -7,8 +7,7 @@ operations require explicit approval and evidence.
|
|
|
|
|
|
|
|
|
|
## Rules
|
|
|
|
|
|
|
|
|
|
- Use `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` only when deployment is
|
|
|
|
|
approved; do not overwrite remote `.env`.
|
|
|
|
|
- Use `SYNC_ENV=0 POSTGRES_DATA_DIR=<absolute> STORAGE_HOST_DIR=<absolute> DEPLOY_DATA_MODE=<existing|initialize> bash deploy/trialrun/deploy.sh` only when deployment is approved; do not overwrite remote `.env`.
|
|
|
|
|
- When `server/src/config.js` reads a new environment key, add the same key to
|
|
|
|
|
`deploy/trialrun/.env.example` before deployment; `deploy/trialrun/deploy.sh`
|
|
|
|
|
runs `node deploy/trialrun/validate-env-contract.js` at step 0 and must fail
|
|
|
|
@@ -31,7 +30,8 @@ operations require explicit approval and evidence.
|
|
|
|
|
### 2. Signatures
|
|
|
|
|
|
|
|
|
|
- Command: `node deploy/trialrun/validate-env-contract.js`
|
|
|
|
|
- Deployment entry: `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
|
|
|
|
|
- Deployment entry requires `SYNC_ENV=0`, absolute `POSTGRES_DATA_DIR`, absolute
|
|
|
|
|
`STORAGE_HOST_DIR` and `DEPLOY_DATA_MODE=existing|initialize`.
|
|
|
|
|
- Contract sources:
|
|
|
|
|
- `server/src/config.js`
|
|
|
|
|
- `deploy/trialrun/.env.example`
|
|
|
|
@@ -47,6 +47,10 @@ operations require explicit approval and evidence.
|
|
|
|
|
allowlist in `validate-env-contract.js`.
|
|
|
|
|
- `SYNC_ENV=0` is the default public deploy mode; it does not overwrite remote
|
|
|
|
|
`.env`.
|
|
|
|
|
- Relative data-path defaults are allowed only for direct local Compose.
|
|
|
|
|
`deploy.sh` requires explicit absolute host paths and a data mode; `existing`
|
|
|
|
|
verifies the PGDATA directory contains `PG_VERSION`, while `initialize` is
|
|
|
|
|
the only mode allowed to create missing directories.
|
|
|
|
|
|
|
|
|
|
### 4. Validation & Error Matrix
|
|
|
|
|
|
|
|
|
@@ -54,6 +58,10 @@ operations require explicit approval and evidence.
|
|
|
|
|
- Service omits `env_file: ./.env` -> contract exits non-zero.
|
|
|
|
|
- Unauthorized compose override -> contract exits non-zero.
|
|
|
|
|
- Remote `.env` missing -> deploy step 4 exits before migration.
|
|
|
|
|
- Missing/relative/unsafe/overlapping data paths or missing data mode -> deploy
|
|
|
|
|
exits locally before provenance, synchronization or SSH.
|
|
|
|
|
- `existing` target missing `PG_VERSION` or storage directory -> remote deploy
|
|
|
|
|
exits before Compose starts DB.
|
|
|
|
|
|
|
|
|
|
### 5. Good/Base/Bad Cases
|
|
|
|
|
|
|
|
|
@@ -83,7 +91,9 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
|
|
|
|
```bash
|
|
|
|
|
# Add the key to deploy/trialrun/.env.example first.
|
|
|
|
|
node deploy/trialrun/validate-env-contract.js
|
|
|
|
|
SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
|
|
|
|
SYNC_ENV=0 POSTGRES_DATA_DIR=/srv/hyb-oa/pgdata \
|
|
|
|
|
STORAGE_HOST_DIR=/srv/hyb-oa/storage DEPLOY_DATA_MODE=existing \
|
|
|
|
|
bash deploy/trialrun/deploy.sh
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Scenario: Public Smoke Navigation Labels
|
|
|
|
@@ -279,7 +289,8 @@ const REQUIRED_ADMIN_NAV_KEYS = ["systemSettings"];
|
|
|
|
|
### 2. Signatures
|
|
|
|
|
|
|
|
|
|
- Provenance gate: `node deploy/trialrun/check-source-provenance.mjs`.
|
|
|
|
|
- Standard release: `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`.
|
|
|
|
|
- Standard release additionally supplies the explicit absolute data-path and
|
|
|
|
|
data-mode contract required by `deploy.sh`.
|
|
|
|
|
- Build/runtime inputs: `BUILD_SHA=<full-commit>` and
|
|
|
|
|
`VITE_BUILD_SHA=<12-char-commit>`.
|
|
|
|
|
- Runtime outputs: `/health.data.buildSha`, `/ready.data.buildSha` and the
|
|
|
|
@@ -606,6 +617,8 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
|
|
|
|
- Every deploy/timer Compose command uses `-p "$COMPOSE_PROJECT_NAME"`.
|
|
|
|
|
- PostgreSQL mount: `${POSTGRES_DATA_DIR:-./data/postgres}`.
|
|
|
|
|
- Application storage mount: `${STORAGE_HOST_DIR:-./data/storage}`.
|
|
|
|
|
- Formal deploy inputs: absolute `POSTGRES_DATA_DIR`, absolute
|
|
|
|
|
`STORAGE_HOST_DIR`, `DEPLOY_DATA_MODE=existing|initialize`.
|
|
|
|
|
- DB has no host `ports`; server remains loopback-only.
|
|
|
|
|
|
|
|
|
|
### 3. Contracts
|
|
|
|
@@ -615,11 +628,22 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
|
|
|
|
- A project-name change and its DB/storage path selection are one atomic,
|
|
|
|
|
separately approved downtime operation. Never start two PostgreSQL instances
|
|
|
|
|
against one PGDATA.
|
|
|
|
|
- Freeze the old project name from `docker compose ls`, not from a directory
|
|
|
|
|
guess. Record immutable image IDs/digests, successful migration count, fixed
|
|
|
|
|
business-table counts, resolved mounts and Caddy target before any write.
|
|
|
|
|
- Relative defaults preserve local trialrun compatibility, but production must
|
|
|
|
|
verify the resolved host paths against the active container mounts before
|
|
|
|
|
any start, backup or migration.
|
|
|
|
|
use explicit absolute command inputs and verify them against active container
|
|
|
|
|
mounts before any start, backup or migration.
|
|
|
|
|
- `existing` is the normal migration mode and refuses a missing PGDATA marker
|
|
|
|
|
or storage directory. `initialize` is only for an approved empty first start;
|
|
|
|
|
only that mode may create data directories.
|
|
|
|
|
- Remote readiness may use host curl, but build-SHA JSON validation runs with
|
|
|
|
|
Node inside the server container; the Docker host does not need Node.
|
|
|
|
|
- For an existing cutover, stop the old PostgreSQL completely, copy PGDATA and
|
|
|
|
|
storage into pre-verified empty absolute targets while preserving ownership,
|
|
|
|
|
permissions, links and extended attributes, and retain the old directories
|
|
|
|
|
untouched and unmounted for the observation period. Do not rename away the
|
|
|
|
|
only pre-migration rollback data and do not copy a running PGDATA.
|
|
|
|
|
|
|
|
|
|
### 4. Validation & Error Matrix
|
|
|
|
|
|
|
|
|
@@ -627,8 +651,12 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
|
|
|
|
- Any fixed `container_name` appears -> environment contract fails.
|
|
|
|
|
- Required data mount loses its parameter -> environment contract fails.
|
|
|
|
|
- Deploy/timer omits `-p` -> environment contract fails.
|
|
|
|
|
- Formal deploy omits paths/mode, uses a relative/broad/overlapping path, or an
|
|
|
|
|
`existing` PGDATA lacks `PG_VERSION` -> fail before DB start.
|
|
|
|
|
- New project resolves the same PGDATA while the old DB runs -> stop before
|
|
|
|
|
Compose start; do not use service health as permission to continue.
|
|
|
|
|
- Old project name was inferred, immutable image ID or baseline counts are
|
|
|
|
|
missing, or target copy is non-empty -> stop the cutover.
|
|
|
|
|
|
|
|
|
|
### 5. Tests Required
|
|
|
|
|
|
|
|
|
@@ -641,9 +669,12 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT}/ready"
|
|
|
|
|
|
|
|
|
|
- Good: one approved project resolves one stopped-and-migrated PGDATA and the
|
|
|
|
|
matching active storage path; deploy, timer and backup all use that identity.
|
|
|
|
|
- Base: local trialrun uses the documented relative defaults under one project.
|
|
|
|
|
- Base: direct local Compose uses the documented relative defaults under one
|
|
|
|
|
project; those defaults are never accepted by formal `deploy.sh`.
|
|
|
|
|
- Bad: start a renamed project before the old DB stops, or let it create a new
|
|
|
|
|
empty directory and accept a green health check as proof of data continuity.
|
|
|
|
|
- Bad: move the only PGDATA into the new path, migrate it in place, then assume
|
|
|
|
|
the old container name alone is a rollback point.
|
|
|
|
|
|
|
|
|
|
### 7. Wrong vs Correct
|
|
|
|
|
|
|
|
|
@@ -677,7 +708,7 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
|
|
|
|
|
|
|
|
|
### 2. Signatures
|
|
|
|
|
|
|
|
|
|
- Paired backup: `DB_TOOL_MODE=docker DB_CONTAINER=<compose-resolved-id> STORAGE_DIR_HOST=<verified-active-directory> ./scripts/backup-all.sh`.
|
|
|
|
|
- Paired backup: `DB_TOOL_MODE=docker DB_CONTAINER=<compose-resolved-id> STORAGE_DIR_HOST=<verified-active-directory> BUILD_SHA=<actual-build-sha> SOURCE_IMAGE_DIGEST=<actual-sha256-image-id> ./scripts/backup-all.sh`; at least one source identity is required.
|
|
|
|
|
- Optional safe batch identifier: `BACKUP_RUN_ID=<safe-id>`.
|
|
|
|
|
- Manifest writer: host `node` is preferred; host `python3` is an equivalent
|
|
|
|
|
fallback for Docker-only runtime machines.
|
|
|
|
@@ -693,7 +724,11 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
|
|
|
|
|
|
|
|
|
- A complete recovery point is one batch directory containing `database.dump`,
|
|
|
|
|
`storage.tar.gz` and a final `manifest.json`. The manifest contains only safe
|
|
|
|
|
relative names, byte sizes, SHA-256 hashes, batch time and optional build SHA.
|
|
|
|
|
relative names, byte sizes, SHA-256 hashes, batch time, actual build SHA when
|
|
|
|
|
known and actual source image digest when supplied.
|
|
|
|
|
- `BUILD_SHA` supplied by the caller takes precedence over local Git fallback.
|
|
|
|
|
On a non-Git runtime host, at least `SOURCE_IMAGE_DIGEST=sha256:<64-hex>` is
|
|
|
|
|
mandatory. Never label a legacy backup with the commit planned for deployment.
|
|
|
|
|
- Manifest generation uses exclusive creation and preserves the same schema
|
|
|
|
|
under Node and Python 3; if neither runtime exists, fail before publishing the
|
|
|
|
|
final batch directory.
|
|
|
|
@@ -718,6 +753,10 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
|
|
|
|
people, rollback point and post-restore `/ready`/permission checks.
|
|
|
|
|
- Copy the whole completed batch to approved off-host/off-disk storage; a batch
|
|
|
|
|
left only on the application host is not accepted recovery evidence.
|
|
|
|
|
- A real-format shadow restore compares successful migration count and a fixed
|
|
|
|
|
set of business-table counts with the pre-backup baseline, compares extracted
|
|
|
|
|
regular-file count with the active storage source, and runs `/ready` using a
|
|
|
|
|
schema-compatible image. `/ready` alone is not recovery evidence.
|
|
|
|
|
|
|
|
|
|
### 4. Validation & Error Matrix
|
|
|
|
|
|
|
|
|
@@ -735,9 +774,14 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
|
|
|
|
only when the explicit target container and required DB variables exist.
|
|
|
|
|
- Neither Node nor Python 3 is available -> fail manifest generation and remove
|
|
|
|
|
the temporary batch; do not publish a partial recovery point.
|
|
|
|
|
- Invalid build SHA/image digest, or neither identity available on a non-Git
|
|
|
|
|
host -> fail before DB or storage backup starts.
|
|
|
|
|
- Deployment `.env`, PostgreSQL container and active application connection do
|
|
|
|
|
not identify the same database/user -> stop the live backup; never guess
|
|
|
|
|
credentials or expose values in diagnostics.
|
|
|
|
|
- Shadow migration count, any fixed table count, attachment regular-file count
|
|
|
|
|
or compatible-image `/ready` differs from baseline -> reject the recovery
|
|
|
|
|
point and do not enter production cutover.
|
|
|
|
|
|
|
|
|
|
### 5. Good/Base/Bad Cases
|
|
|
|
|
|
|
|
|
@@ -745,6 +789,8 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
|
|
|
|
host, and a disposable shadow drill restores the DB marker and attachment hash.
|
|
|
|
|
- Good: a Docker-only host uses Python 3 to write the same manifest contract
|
|
|
|
|
after the active application connection is matched to the database container.
|
|
|
|
|
- Good: a legacy container without `/ready.data.buildSha` records its inspected
|
|
|
|
|
`sha256:` image ID and leaves `buildSha` null rather than inventing provenance.
|
|
|
|
|
- Base: backup succeeds locally but off-host copy and deployed `/ready` evidence
|
|
|
|
|
remain explicitly pending; do not claim formal recovery readiness.
|
|
|
|
|
- Bad: source a stale deployment `.env` and treat a failed dump as a prompt to
|
|
|
|
@@ -759,7 +805,7 @@ DB_TOOL_MODE=docker DB_CONTAINER="${DB_CONTAINERS[0]}" \
|
|
|
|
|
- Negative assertions for wrong DB confirmation, corrupt DB/tar, non-empty
|
|
|
|
|
target and dangerous tar path.
|
|
|
|
|
- Validate manifest relative paths, absence of connection secrets and matching
|
|
|
|
|
SHA-256 values.
|
|
|
|
|
SHA-256 values, explicit build-SHA precedence and image-digest preservation.
|
|
|
|
|
- Run the synthetic drill with Node available and with a `PATH` that excludes
|
|
|
|
|
Node but includes Python 3; both paths must complete the same round trip.
|
|
|
|
|
- Real-instance restore and `/ready` remain separately approved evidence and
|
|
|
|
@@ -783,6 +829,7 @@ source deploy/trialrun/.env
|
|
|
|
|
# Before an approved live backup, compare the selected DB container identity
|
|
|
|
|
# with an active app connection using redacted boolean diagnostics.
|
|
|
|
|
DB_TOOL_MODE=docker DB_CONTAINER=<verified-container> \
|
|
|
|
|
STORAGE_DIR_HOST=<verified-active-storage> ./scripts/backup-all.sh
|
|
|
|
|
STORAGE_DIR_HOST=<verified-active-storage> \
|
|
|
|
|
SOURCE_IMAGE_DIGEST=<verified-sha256-image-id> ./scripts/backup-all.sh
|
|
|
|
|
# Real restore remains an approved incident operation after shadow evidence.
|
|
|
|
|
```
|
|
|
|
|