# Stardew mod-sync client

## Opt-in v2 profile

Default launches still use the original root feed and `Mods`. The new opt-in
installer installs profile-aware custom SMAPI/launchers and a separate shortcut
using `--fjord-profile v2`. On macOS the desktop shortcut is a small Finder
`.app` wrapper so launching it doesn't open Terminal before the iTerm handoff.
This selects `/v2/`, `Mods-v2`, `ConfigFiles-v2`, and
separate `StardewValley-v2` non-save app data. Both profiles intentionally use
the standard Steam-managed `StardewValley/Saves` directory. See
`../docs/2.0-profiles.md` and
`../source/BootstrapManager/installers/README.md` for setup/recovery.

The descriptions below of default paths apply to the legacy profile. V2 requires
a profile-tagged manifest and uses separate update state and same-profile
fallback. A custom `--mods-path` is not automatically a managed update target.

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.
  On macOS it reuses a running iTerm2 window with a new tab, starts iTerm2 when
  needed, and falls back to the default `.command` terminal if iTerm2 is absent
  or its Automation request fails;
  `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 production feed may use HTTP Basic authentication over HTTPS. Credentials
are configured by the v2 installer, live in the OS user configuration directory
outside all Mods directories, and are sent only to the exact pinned
`https://stardew.thetorg.org` origin. Authenticated redirects are rejected.
See [`docs/private-feed.md`](../docs/private-feed.md). Access control does not
resolve the independently unsigned-manifest BOOT-003 gate.

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.
