Feature request: Allow atomic placement of offered games with AutoSync disabled

オープン
#40 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@Seagulls がすでに取り組んでいます。

2026年10月9日 から。

  • #43 @Seagulls による — オープン

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
22/100
issue の種類
機能追加
明瞭さ
おおむね明確
活発さ
活発
技術スタック
go
領域
api, backend, database

調査の方向性

The placement path starts at POST /api/offered-games/{gameId}/place, which calls Daemon.TrackGame(), where AutoSync is currently set to true at creation. Read that function and the sync trigger paths to see where a disabled-on-creation state must already hold before any transfer begins. Done means the maintainers have settled the hold-versus-settings-API question, and the work lands with tests for omitted and false autoSync. Draft #43 already covers this, so check with maintainers before writing a competing patch.

索引モデルが issue の本文から書いたものです。

説明

Update — draft implementation

A draft pull request is open: https://github.com/Liquid-co/OpenSave/pull/43

It is not merged. Maintainer review of the API split below is explicitly welcome.

The original request is unchanged and is kept further down. The draft does not implement it as a plain autoSync column write, because that flag only stops this device from starting a sync. A paired device that already knows the game id can still request the manifest and move files. Pause does not survive a daemon restart.

What the draft adds, all opt-in

Ordinary placement and tracking are unchanged when the new fields are omitted.

POST /api/offered-games/{id}/place and POST /api/games accept "autoSync": false or "provisioningHold": true. Either one records the game and a provisioning hold in the same transaction. While the hold is set, that game is excluded from inbound and outbound transfer. A failed hold read is not treated as "not held".

An explicit id is accepted only on held creation, so another machine can be given the same id locally. That does not create the game on the remote peer by itself.

Named save locations can be configured while the hold is in place. A held primary path cannot be changed to a symlink.

Difference from the request below

The original note asked to enable sync later through the existing game-setting API. The draft does not do that.

PATCH of autoSync cannot clear the hold. The unblock is:

POST /api/games/{id}/release-provisioning
  • An empty body clears the hold and turns AutoSync on. Release itself does not call SyncGame. A later reconcile or reconnect can transfer.
  • {"autoSync": false} clears the hold and leaves local AutoSync off. An explicit sync can still run.

[!WARNING]
Staged release is not a peer lock. After the hold is cleared, an unheld peer can still request the save even if this device's AutoSync is off. Peers that must not exchange bytes have to stay held.

A stock or older daemon on the same database does not understand the hold. It was observed to serve a held manifest (HTTP 200). Do not downgrade a data directory that has provisioning holds. A stock client talking to a patched held server was refused for the tested manifest and delete requests. That is not general mixed-version safety.

An already tracked game cannot be converted into a hold. That is intentional.

Suggested acceptance criteria, against the draft
  • Placing or tracking without the new fields keeps current behaviour. Covered by tests in #43.
  • autoSync: false or provisioningHold: true creates the game and the hold together, before any sync.
  • While held, reconcile, restart, direct sync, and inbound manifest/delete do not move that game's saves.
  • Named save locations can be configured before release.
  • Enabling sync later uses release-provisioning, not PATCH autoSync. This is a deliberate difference from the request below. Maintainer preference is still open.
  • Empty-body release turns AutoSync on. {"autoSync": false} does not, and does not stop an unheld peer from requesting the save.
  • Offer identity, offer clearing, and existing path/security checks are preserved.
  • Downgrade safety is not met. Old binaries do not enforce the new table.

Summary

Would it be possible to extend the existing offered-game placement API to optionally create a tracked game with AutoSync disabled from the outset, rather than enabling synchronization during creation and requiring a second operation to disable it?

This would be particularly useful for headless OpenSave installations, NAS devices, home servers, and third-party applications that manage game-save provisioning programmatically.

Current Behaviour

OpenSave already provides a useful mechanism for handling games received from paired devices when the local save directory is unknown:

  • opensave offers
  • opensave offers place <gameId> <folder>
  • POST /api/offered-games/{gameId}/place

However, the placement endpoint calls Daemon.TrackGame(), which currently sets AutoSync = true during game creation.

For automated provisioning, this introduces a race condition:

  1. An application detects an offered game from a paired device.
  2. It creates a suitable local storage directory.
  3. It accepts the offer through OpenSave's placement API.
  4. OpenSave creates the tracked game with AutoSync enabled.
  5. The application attempts to disable AutoSync while configuring additional save locations, verifying identity, or performing other setup.

Between steps 4 and 5, synchronization may already have started.

This makes it difficult to safely automate provisioning on an already-paired headless device.

Proposed Solution

Extend the existing offered-game placement endpoint to accept an optional autoSync property.

Example request:

POST /api/offered-games/example-game/place
Content-Type: application/json

{
  "path": "/srv/opensave/games/example-game/main",
  "autoSync": false
}
Expected Behaviour
  • When autoSync is omitted, preserve the existing behaviour for backward compatibility.
  • When autoSync: false is supplied, create the tracked game with synchronization disabled atomically.
  • Ensure no initial peer sync, watcher-triggered sync, or other automatic transfer can begin before the disabled state is established.
  • Preserve the existing game ID, offered-game lifecycle, path validation, and security protections.
  • Allow synchronization to be enabled later through the existing game-setting API or CLI.

The important requirement is that this must be atomic within the tracking lifecycle. Creating the game with AutoSync enabled and immediately issuing a separate request to disable it would not eliminate the race.

Example Use Case

Consider an always-on Linux server running OpenSave as a persistent synchronization and backup peer.

A gaming PC tracks a new game, and the server receives an offered-game notification.

A local management application needs to:

  1. Allocate a dedicated storage directory.
  2. Accept the offered game with AutoSync initially disabled.
  3. Configure additional named save locations, if required.
  4. Verify the local storage layout and game identity.
  5. Explicitly enable AutoSync once provisioning is complete.

Currently, step 2 can initiate synchronization before the remaining setup has finished.

An atomic disabled-on-creation option would allow this workflow without modifying OpenSave's database, stopping the daemon, or introducing a separate server mode.

Scope and Backward Compatibility

Ideally, this would be a small, backward-compatible extension.

In scope:

  • An optional autoSync parameter on the existing placement endpoint.
  • Support for initializing the tracked game with AutoSync disabled before any synchronization can begin.
  • Preservation of existing behaviour when the parameter is omitted.
  • Regression tests covering disabled-on-creation behaviour.

Out of scope:

  • New networking protocols.
  • Changes to the P2P synchronization algorithm.
  • Dedicated server or NAS modes.
  • Automatic directory allocation.
  • Changes to conflict resolution or snapshot formats.
  • Changes to existing security or authentication rules.

Question for Maintainers

Would you be open to a narrowly scoped PR implementing this?

Draft #43 is that attempt, with the hold/release split described at the top. Feedback on whether release should instead be the existing settings API is welcome.

主要言語
Go
スター
225
フォーク
9
平均マージ
1日 23時間
マージ済み PR(30日)
76

環境構築

  • Dockerfile または Docker Compose ファイルあり
  • プルリクエストのテンプレートなし
  • コントリビューションガイドなし

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Liquid-co/OpenSave のほかの issue

Liquid-co/OpenSave の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。