> For the complete documentation index, see [llms.txt](https://docs.kouch.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kouch.dev/docs/adr/0016-steam-library-plugin.md).

# ADR 0016: The Steam library is a first-party Kouch plugin

## Status

Accepted on 2026-09-26. It supersedes ADR 0014's companion program and FEATURE\_PLAN's "external GitHub companion tool" for Steam shortcuts.

The owner:

* asked for the feature: "fork steam rom manager for the images of the games, image data, game data, descriptions, information, add to steam shortcuts and launching via kouch";
* then, after the companion was built: "i wanted inside of kouch built in";
* then: "or well, make it a plugin for kouch".

## Context

* **What it does.** Each game gets its own Steam tile, with artwork and categories, and opens through Kouch (`--launch=<stable id>`). Steam has no API for this. Tools write the binary `userdata/<account>/config/shortcuts.vdf` and `config/grid/` while Steam is closed, because Steam rewrites the file on exit.
* **The companion doesn't fit.** The shortcut manager forked for ADR 0014 is GPL-3.0 TypeScript/Electron. Its code can't go into Kouch, and the owner wants the feature inside Kouch, not beside it.
* **Plugins fit.** Kouch already has sandboxed WASM plugins (ADR 0011): per-session, capability-checked, off until the user enables them.

## Decision

1. **The feature is the first-party plugin `steam-library`** (`plugins/steam-library/`). It is written in Rust and built to `wasm32-unknown-unknown`, ships inside Kouch, and starts disabled like every plugin.
   * Its logic comes from a pure crate, `crates/kouch-shortcuts`. That crate covers binary VDF read/write (byte-identical round-trip), shortcut app ids (crc32 of exe + name, high bit set), grid file names, unique names per game, and the sync plan (add, update and remove only Kouch-managed entries).
   * The crate compiles to wasm32 and to native, and reimplements the published data formats only. No code is taken from the GPL tool.
2. **Plugin ABI v2 adds, for app-level plugins** (`"scope": "app"`, loaded at startup when enabled):
   * `app_actions`: rows on the plugin's page in Settings › Plugins, delivered as `{"type":"app_action","id":…}`.
   * `library_read`: `library_list` returns the games Kouch knows (stable id, title, system, the art files Kouch has, game information). Game file paths are left out.
   * `steam_shortcuts`: host-mediated access, so the plugin never writes files itself.
     * The plugin reads the current `shortcuts.vdf` and stages a new one, plus grid files from the library's own art.
     * The host checks that every entry that isn't Kouch's is byte-identical and that grid names and sources are valid, then asks the user to confirm.
     * After confirmation the host runs the apply: a detached helper closes Steam, backs up, writes, reopens Steam, and reopens Kouch.
3. **Game information** (description, developer, publisher, release date, genres, players, rating) stays a Kouch feature: read from the user's `gamelist.xml`, with opt-in online providers later, and shown on the game page. The plugin copies it into nothing; Steam has no field for it.
4. **What happens to the companion.** The fork (`golfista/kouch-srm`, private) is kept as a reference and for scratch dry runs. It is no longer the product path. Its finding carries over: shared titles collapse into one shortcut unless they are made unique.
5. **One Steam collection per system, named after it** (owner, 2026-09-26: "it needs to add it to its own library, and the library is the console name"). The system name comes from the user's profile at run time, and Kouch's code names no system.
   * Steam keeps collections in `userdata/<account>/config/cloudstorage/cloud-storage-namespace-<n>.json`. The active `<n>` comes from `cloud-storage-namespaces.json`.
   * The helper writes that file itself, after `shortcuts.vdf`, while Steam is closed. The plugin has no say: the wanted collections come from the written `shortcuts.vdf` (Kouch's entries grouped by their system tag), and the file is backed up first.
   * Kouch owns only collections whose id starts with `kouch-` (the prefix plus the name in URL-safe base64). Every other entry is kept as its exact JSON text. A Kouch collection that is no longer wanted, and every one on Remove, is marked `is_deleted` rather than dropped, so Steam's cloud copy doesn't bring it back. A user with no Kouch collection and nothing to add gets no file created.
   * If the collections fail after the shortcuts were written, the result says so: the games changed, their collections didn't.
   * On the Deck the Decky plugin makes the same collections live through Steam's in-client collection store.
6. **Rule 7 during development.** No write to the owner's real Steam: tests use scratch `userdata` trees. The first live write waits for the owner's go, and the host's backup runs first.

## Consequences

* The feature lives inside Kouch, in the plugin model the owner asked for. It can be turned off, and its reach is limited to what the host checks.
* ABI v2 opens app-level plugins to third parties too. `steam_shortcuts` gives no raw file access and can't change shortcuts that aren't Kouch's, whoever writes the plugin.
* **Collections rely on Steam's merge.** Steam merges the namespace file with its cloud copy when it starts (`strMethodId: "union-collections"`), the same thing the shortcut managers rely on. Not yet ruled out: a cloud copy newer than Kouch's write could drop the new collections on the first start. The first live run on Windows checks this, and Remove's `is_deleted` entries depend on the same merge.
* Steam must restart to take the change. In Deck Game Mode the host refuses unless the restart is known to be safe; Desktop Mode and Windows are fine.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kouch.dev/docs/adr/0016-steam-library-plugin.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
