Bundle
@ddtcorex/dsh-maestro-sync
Maestro harness sync — merge memories and sessions across machines (publishable)
- Source
- ddtcorex
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 3 days ago
Readme
# dsh-maestro-sync
Maestro harness sync — merge memory and sessions across machines (publishable)
> DSH Maestro plugin — part of the `dsh-maestro-*` ecosystem (`@ddtcorex/dsh-maestro-sync`).
## Install
```sh
dsh plugin add @ddtcorex/dsh-maestro-sync
```
## Safe Sync — Preview then Apply
Sync is **exact, read-only preview first, then confirmed apply**:
```sh
# 1. Preview (read-only, no writes, 60s TTL) — the only way to see a plan
node lib/cli.js --pull --dry-run # or --push --dry-run
# stdout: one final JSON SyncPreview { ok, previewId, revision, expiresAt, summary, actions }
# human progress goes to stderr
# 2. Apply the EXACT preview you just reviewed (requires all three)
node lib/cli.js --pull --apply --preview-id <id> --confirm
```
- **No omitted boolean can apply a sync.** `--apply` without `--preview-id` and
`--confirm` exits non-zero; the legacy `pull`/`push` routes and tools are
preview-only compatibility aliases and never write.
- **Stale-guard:** apply re-inventories both machines, recomputes the plan and
rejects it as `STALE_PREVIEW` if anything changed since the preview — no write
happens against a stale plan. Apply is single-use per preview id.
- **Eligible only:** `dsh-maestro-memory/**/*.md` (no `*.bak.*`), `dsh-maestro-memory/SUGGESTIONS.jsonl`, `sessions/<hash>/<id>/session.jsonl.zstd`
- **Transport:** argv-only `spawn`/`rsync --files-from`, no shell interpolation;
the remote root is a validated absolute path. A `~/.dsh` default is resolved
to the absolute remote home by the SSH preflight (`printf %s '$HOME'`), never
by shell `~` expansion.
- **Sessions:** `Buffer`/`path` only via validated Zstd artifact API; the
standalone checksummed header frame is preserved and merged line-union.
- **Atomic publish:** pull = `backup + fsync(tmp) + rename + fsync(dir)` per
local file; push = materialize to a private operation dir, upload to
`<root>/.maestro-sync/stage/<op>/`, then a fixed POSIX CAS helper validates
each target SHA-256 (`expectedTargetSha256`), backs up and renames atomically.
A concurrent remote change is reported as `CONCURRENT_MODIFICATION` and never
overwrites the target.
- **Fail closed:** a transport/stage/publish failure is a structured non-zero
result with `committed`/`uncommitted` journals — `ok:true` only when every
reported file was actually published. No merge-mode fallback to destructive
rsync; `--strategy=override` exists only with a separate `--ack-override`.
- **Recovery:** every overwritten file keeps a timestamped backup beside it
(`.bak.<ts>.<rand>`; remote backups under the same rule). Restore with
`cp <path>.bak.* <path>`.
- **Consent:** live Apply is an operator action — the CLI requires
`--preview-id` + `--confirm`; the Settings UI only offers Apply inside a
confirmation dialog bound to a live preview.
- **Host preflight:** `ssh -o ConnectTimeout=5` must succeed before preview/apply.
- **UI:** Settings -> Maestro Sync -> *Preview Pull/Push* -> review
`copy`/`merge`/`skip`/`conflict` -> confirmation dialog (direction, host,
plan age, action counts) -> *Apply*.
Excluded (never read, hashed or copied): settings, tunnel profiles, secret
material, profiles, supervisor state, storages, tools, skills, logs, caches and
`*.bak.*`.
## R2 Sync — offsite backup (Cloudflare R2; AWS S3 via the same client, UI hidden)
Backup and restore of the eligible data (memory + session logs) to an
S3-compatible bucket through a dependency-free SigV4 client.
- **Config** (`~/.dsh/maestro/settings.json` → `domains.sync.r2`): `accountId`,
`bucket` (default `maestro-backup`), `prefix`, `region`; optional
`provider: "aws"` with a real region works through the same client (UI
hidden in phase 1).
- **Secret material**: environment (`R2_ACCESS_KEY_ID`/`R2_SECRET_ACCESS_KEY`,
AWS `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`) or a private `0600` sidecar
file in the plugin's own runtime dir — never in settings, never logged,
never returned by RPC/tools (status shows only `Env | Private file | Not
configured` and the bucket/prefix).
- **Preview Backup** is read-only: it compares current eligible hashes against
the last manifest in the bucket (no object transfer). **Apply** is the only
upload route: it PUTs missing blobs (content-addressed, idempotent), writes
an immutable manifest and CAS-advances the `HEAD` pointer; `ok` is reported
only after `HEAD` advances (`CONCURRENT_MODIFICATION` on a race).
- **Restore**: to a new directory (never touches the live home) or in place
(each overwritten target keeps a `.bak.<ts>.<rand>`, `fsync+rename`), both
confirmation-first. **GC**: retains the newest 30 daily + 12 monthly
manifests and deletes only unreachable blobs, confirmation-first.
- Live R2 conditional-write behavior is pinned by an operator-consent probe
after the phase-3 hermetic fake-S3 gate — no R2 account is needed to build
or test this feature.
## Develop
```sh
pnpm --filter @ddtcorex/dsh-maestro-sync verify
pnpm --filter @ddtcorex/dsh-maestro-sync build
pnpm --filter @ddtcorex/dsh-maestro-sync test
```
Install
dsh plugin --profile web add github:ddtcorex/dsh-maestro-sync
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install ddtcorex-dsh-maestro-sync from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.