# Stardew mod-sync client

The client runs before SMAPI on receiving/player computers and synchronizes the
game's `Mods` directory from `https://stardew.thetorg.org/` by default. The
website installers put it at `<game>/.stardew-modsync` and update the managed
Steam launch path; players only need to launch Stardew normally afterward.

The editable production payload is owned by
`source/BootstrapManager/bootstrap`. The corresponding files under this
`client/` directory are byte-for-byte compatibility mirrors generated for the
existing installer and schema-1 staging tools. Run
`source/BootstrapManager/prepare-package --check-client` before packaging, or
use its explicit `--sync-client` option after reviewing canonical source
changes.

- macOS/Linux clients: the installer makes Steam's `StardewValley` wrapper call
  `<game>/.stardew-modsync/unix/launch-stardew`. After synchronization, that
  launcher opens SMAPI in a visible terminal when Steam didn't provide one;
  `SMAPI_NO_TERMINAL=true` retains SMAPI's explicit no-terminal behavior.
- Windows: the installer keeps the previous `.thors-fjord/launch.ps1` Steam
  launch option working and refreshes the Thor's Fjord desktop shortcut.

Ordinary launches are fail-open. If Python or PowerShell synchronization fails
because of DNS, HTTP, manifest, archive, hash, or local I/O errors, the launcher
records a best-effort redacted `.stardew-modsync/last-error.log` and starts the
last transactionally completed installed SMAPI/mod set. A normal launch fails
only when its installed SMAPI executable is unavailable. Synchronization still
downloads and validates every changed archive before changing `Mods`, and a
failed activation retains the old files or a clearly named recovery
transaction instead of deleting the only backup.

To disable remote updates on the source computer, create this file in the
Stardew game directory (beside `Mods` and `StardewModdingAPI`):

`thors-fjord-update.txt`

Set its content to:

`disable remote update`

The normal launcher will then skip all webserver access and launch the locally
installed mods. This avoids a source-to-webserver-to-source loop without a
custom launcher. Delete the file, empty it, or set it to `enable remote update`
to resume normal synchronization. The setting is case-insensitive and ignores
surrounding or repeated whitespace.

Set `STARDEW_MODSYNC_FEED` to another URL or a local staged `webserver`
directory for testing. The Python client accepts `--feed`, `--game-dir`,
`--dry-run`, and `--validate-only`; the PowerShell client accepts `-Feed`,
`-GamePath`, `-DryRun`, `-NoLaunch`, and `-ValidateOnly`. Those direct,
installer, dry-run, update-only, and validation operations remain fail-closed:
they return a synchronization failure and never silently launch the game. The
Unix wrapper exposes `--modsync-update-only`, `--modsync-dry-run`, and
`--modsync-validate-only` for the same explicit behavior.

The last successfully installed ZIP hashes are stored in
`<game>/.stardew-modsync-manifest.json`, outside `Mods`. A synchronization
downloads and validates all changed archives before changing any mod directory,
then applies removals and replacements transactionally.

## BootstrapManager and recovery

`ThorFjord.BootstrapManager` is a permanent ordinary SMAPI mod delivered by the
unchanged schema-1 mod manifest. It has no network client. It compares the
bundled bootstrap version, byte lengths, SHA-256 hashes, platform applicability,
and Unix modes; a newer approved payload is staged beside `.stardew-modsync`
and activated only after the current game process exits. Pending state, the
activation log, and retained `.stardew-modsync.old.*` rollback directories stay
outside the directory being replaced. On Windows the stable
`.thors-fjord/launch.ps1` is included in the transaction. On Unix the outer
`StardewValley` wrapper continues to target the stable launcher and falls back
to installed SMAPI if an interrupted swap temporarily leaves that launcher
missing.

For manual recovery, first close Stardew, SMAPI, Steam launch processes, and any
PowerShell synchronizer. Preserve the failed live directory for diagnosis. If
`<game>/.stardew-modsync` is missing or invalid, rename the retained
`<game>/.stardew-modsync.old.<id>` named by the pending activation state back to
`<game>/.stardew-modsync`. On Unix restore the matching retained
`<game>/.stardew-bootstrap-manager/outer.<id>.old` to
`<game>/StardewValley` if the outer launcher is missing or invalid, then set
mode `0755` on `StardewValley`,
`.stardew-modsync/modsync.py`, and
`.stardew-modsync/unix/launch-stardew`. On Windows restore that same matching
`<game>/.stardew-bootstrap-manager/outer.<id>.old` file to
`<game>/.thors-fjord/launch.ps1` if needed. Always move an existing suspect
target aside before restoring; do not overwrite or delete the only backup.
Files named `outer.<id>.failed` or `outer.<id>.partial.*` and
`.stardew-modsync.failed.*`/`.partial.*` are retained diagnostics, not the
preferred known-good rollback copy. The existing
`StardewValley.before-modsync` remains an installer-era recovery copy. Creating
`thors-fjord-update.txt` with `disable remote update` remains the manual way to
bypass a failing remote feed while using the installed set.

An existing client must complete one successful legacy synchronization and
start SMAPI once to download and run BootstrapManager. A client already unable
to reach SMAPI cannot receive this repair through that broken path and needs the
recovery installer or a manual restore. The embedded payload hashes prove
package consistency only; the outer schema-1 feed is still unsigned, so
BOOT-003 remains unresolved.
