Feature request: Allow atomic placement of offered games with AutoSync disabled
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 22/100
調査の方向性
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: falseorprovisioningHold: truecreates 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, notPATCH 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 offersopensave 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:
- An application detects an offered game from a paired device.
- It creates a suitable local storage directory.
- It accepts the offer through OpenSave's placement API.
- OpenSave creates the tracked game with AutoSync enabled.
- 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
autoSyncis omitted, preserve the existing behaviour for backward compatibility. - When
autoSync: falseis 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:
- Allocate a dedicated storage directory.
- Accept the offered game with AutoSync initially disabled.
- Configure additional named save locations, if required.
- Verify the local storage layout and game identity.
- 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
autoSyncparameter 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 ファイルあり
- プルリクエストのテンプレートなし
- コントリビューションガイドなし
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Liquid-co/OpenSave のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
Liquid-co/OpenSave#149 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 20/100
メンテナーはふだん 1 日以内に返信
-
bug(native): untracked backup overwrite can change another game's owned save path対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
メンテナーはふだん 1 日以内に返信
-
bug(native): delayed sync-complete can contaminate a re-added named root's lineage対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
メンテナーはふだん 1 日以内に返信
Liquid-co/OpenSave の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
nebari-dev/nebi#652 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
govulncheck reports 7 reachable vulnerabilities in dependencies対応中かも @dwisiswant0 が今日担当しました。 オープンType: Bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
projectdiscovery/rawhttp#558 ·
-
govulncheck reports vulnerable golang.org/x/net and goldmark in called code対応中かも @dwisiswant0 が今日担当しました。 オープンType: Bug
難易度 1/5 1時間未満 初心者へのやさしさ 72/100
projectdiscovery/cdncheck#549 ·
メンテナーはふだん 6 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 83/100
メンテナーはふだん 1 日以内に返信
-
Type: Bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
projectdiscovery/pdtm#467 ·