Docs updated.
This commit is contained in:
parent
f5cd8d88eb
commit
60409b9014
1 changed files with 82 additions and 0 deletions
82
README.md
82
README.md
|
|
@ -81,6 +81,88 @@ or `starter`.
|
|||
Admin UI: `/admin/dev-deploy/` (logged in as an admin). Machine API:
|
||||
`/dev-deploy/api/{journal,ingest,file/:uuid,health}` (HMAC-signed peer requests).
|
||||
|
||||
### Pairing two instances
|
||||
|
||||
Do this once per pair (e.g. MAIN ↔ TEST). Each side needs the other's
|
||||
`env_id` (a UUID shown on its `/admin/dev-deploy/` dashboard) and base URL.
|
||||
|
||||
1. **On instance A** — go to `/admin/dev-deploy/peers`. Under "Add peer", fill
|
||||
in B's `env_id`, a label, B's base URL, and leave "Existing secret" blank.
|
||||
Click **Pair**. A's response shows a 64-hex shared secret **once** — copy it.
|
||||
2. **On instance B** — `/admin/dev-deploy/peers` → "Add peer". Fill in A's
|
||||
`env_id` + base URL, paste the secret from step 1 into "Existing secret",
|
||||
click **Pair**.
|
||||
|
||||
Both sides now have a peer row with a sealed copy of the shared secret. The
|
||||
plain secret is never persisted on either side — only the AES-256-GCM
|
||||
ciphertext (KEK derived from `SALTCORN_SESSION_SECRET`). Use **Rotate** on
|
||||
either side to roll the secret (the peer page surfaces the new one once;
|
||||
paste it on the other side via Rotate).
|
||||
|
||||
### Promoting changes (source → target)
|
||||
|
||||
1. Make metadata edits on the source (e.g. create a table on MAIN).
|
||||
2. Go to `/admin/dev-deploy/plan`, pick the target peer, click **Show plan**.
|
||||
Lists the ops that would be sent (everything since the last outbound
|
||||
anchor for that peer).
|
||||
3. Click **Promote N ops**. The target applies them, advances the outbound
|
||||
anchor, and returns per-op results (applied / error / conflict counts in
|
||||
the redirect flash).
|
||||
|
||||
Plugin-list mismatches between source and target are surfaced as warnings in
|
||||
the same flash message (e.g. "peer missing plugin X", "plugin version mismatch
|
||||
on Y"). They don't block the promote — they're advisory.
|
||||
|
||||
### Pulling changes (target ← source)
|
||||
|
||||
On the target, `/admin/dev-deploy/peers` → **Pull** on the source's peer row.
|
||||
Fetches everything since the last inbound anchor and applies it. If any op
|
||||
conflicts with a local op on the same entity since the last sync, the incoming
|
||||
op is journaled with `status='conflict'` and **not** applied; the flash
|
||||
redirects to `/admin/dev-deploy/conflicts`.
|
||||
|
||||
### Resolving conflicts
|
||||
|
||||
A conflict means both sides edited the same entity since the last sync. On
|
||||
the target side at `/admin/dev-deploy/conflicts`, each pending conflict shows
|
||||
the incoming op (theirs) next to the local op (mine):
|
||||
|
||||
- **Use theirs** — apply the incoming op now, overwriting the local change.
|
||||
The local op stays in the journal but its effect is gone.
|
||||
- **Use mine** — mark the incoming op `rejected`. Local state stands. The
|
||||
peer keeps trying to ship the op on future pulls; we skip it by op_id.
|
||||
- **Merge per field** (only for `update_X` vs `update_X` conflicts) — opens
|
||||
a form with each diverging field. Pick **keep current**, **take incoming**,
|
||||
or type a custom value per field. Submitting marks the conflict `merged`.
|
||||
|
||||
### Per-table data mode
|
||||
|
||||
By default, table **rows** are local-only (`user` mode) and dev-deploy only
|
||||
migrates the table's schema. Change this per-table at `/admin/dev-deploy/tables`:
|
||||
|
||||
- **user** — rows belong to each environment; never synced. The Saltcorn
|
||||
`users` table is hard-locked to this mode.
|
||||
- **starter** — on the first promote, ships current rows once. The target
|
||||
then owns them; future row changes on the source don't propagate.
|
||||
- **managed** — rows always sync from source. Source is canonical; target's
|
||||
row edits get overwritten or surface as row conflicts.
|
||||
|
||||
Switching to `managed` or `starter` adds a hidden `_dd_row_uuid` column to
|
||||
the table via raw `ALTER` (not registered in `_sc_fields`, so Saltcorn's
|
||||
table builder doesn't show it) and ships current rows in the next promote.
|
||||
Switching back to `user` drops the column (best-effort; older SQLite without
|
||||
DROP COLUMN support will error). FKs to user-mode tables are NULL'd on the
|
||||
target with a warning attached to the op.
|
||||
|
||||
### Reverting an op
|
||||
|
||||
The journal at `/admin/dev-deploy/ops` shows every op with a **Revert** button.
|
||||
Revert appends a compensating op (`create_X` → drop; `drop_X` → recreate from
|
||||
the captured before-snapshot; `update_X` → re-apply before-snapshot as a
|
||||
patch) rather than rewriting history. The inverse op promotes to peers like
|
||||
any other op on the next push. Reverting a `drop` produces a new entity with
|
||||
a fresh UUID — content is restored, identity is not.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue