> 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/for-creators/creators/plugins.md).

# Write a plugin

Write a Kouch plugin: a small sandboxed WebAssembly module with a manifest, capabilities and a few host calls.

A plugin adds a feature to Kouch: Quick Menu actions, reactions when a game starts or stops, the emulator's own hotkeys pressed at the right moment, or, for app plugins, tools of their own. Plugins are small **WebAssembly** modules. They run sealed off, with no files, network, clock or environment of their own. They can only call Kouch's host functions, and only the ones the user approved. See [Plugins](/mods-and-plugins/plugins.md) for what the user sees.

A working example is **Save slots**: two Quick Menu actions that send the emulator's quick-save and quick-load keys.

## The package

A plugin is a folder:

```
my-plugin/
  plugin.json
  plugin.wasm
```

```json
{
  "id": "my-plugin",
  "name": "My plugin",
  "version": "1.0.0",
  "api": 1,
  "entry": "plugin.wasm",
  "description": "One sentence shown in Settings.",
  "capabilities": ["quick_menu", "hotkeys", "toasts"],
  "profiles": []
}
```

* `id`: 1–64 characters from `a-z 0-9 - _ .`
* `entry`: the `.wasm` file inside the folder, 8 MB at most.
* `profiles`: the emulator profile ids it applies to. Empty means every emulator.
* Unknown fields are refused.

It's installed from a folder in Kouch's `plugins` folder, from a `.kod` with `plugins/<id>/`, or from the Workshop (with Kouch's Steam release). **Every plugin starts off.** Turning it on approves exactly the capabilities listed; a later version that asks for more is turned off until the user approves again.

## Capabilities

| Capability        | Allows                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------- |
| `quick_menu`      | `menu_action`: add rows to the Quick Menu while a game runs                                 |
| `hotkeys`         | `hotkey`: send the emulator a key combination                                               |
| `toasts`          | `toast`: show a short message                                                               |
| `settings`        | `setting_get` / `setting_set`: up to 64 small text settings, kept between sessions          |
| `files_saves`     | `file_read` / `file_write` under `saves/`, the emulator's saves folder                      |
| `files_storage`   | `file_read` / `file_write` under `storage/`, the emulator's storage folder                  |
| `session_events`  | reserved: game start and stop are always delivered                                          |
| `prelaunch`       | reserved: options on the pre-launch sheet                                                   |
| `app_actions`     | API 2, app plugins: buttons on the plugin's row in Settings › Plugins                       |
| `library_read`    | API 2: the game list (titles, systems, art, game information; **never file paths**)         |
| `steam_shortcuts` | API 2, app plugins: propose changes to Kouch's own Steam shortcuts, which the user confirms |

File paths are relative, like `saves/slots/1.txt`. A path with `..`, a drive, or a `bios` or `system` part is refused. **A plugin can never reach BIOS, firmware or keys.** A call without its capability returns `-2`, is reported to the user, and is never carried out.

## API version 1

Data crosses between Kouch and the plugin as UTF-8 JSON or text in the module's memory.

**The module exports:**

| Export           | Signature                     | Purpose                                                      |
| ---------------- | ----------------------------- | ------------------------------------------------------------ |
| `memory`         | memory                        | where Kouch writes events                                    |
| `kouch_api`      | `() -> i32`                   | returns `1`                                                  |
| `kouch_alloc`    | `(len: i32) -> i32`           | returns a pointer to `len` writable bytes for the next event |
| `kouch_on_event` | `(ptr: i32, len: i32) -> i32` | handles one event; returns `0`                               |

**It imports, from module `kouch`:**

| Import        | Signature                               | Notes                                                             |
| ------------- | --------------------------------------- | ----------------------------------------------------------------- |
| `log`         | `(ptr, len)`                            | goes to Kouch's log                                               |
| `toast`       | `(ptr, len) -> i32`                     | text                                                              |
| `hotkey`      | `(ptr, len) -> i32`                     | a JSON list of 1–4 key names, like `["shift","f1"]`               |
| `menu_action` | `(ptr, len) -> i32`                     | `{"id":"save","label":"Quick save"}`; label up to 80 characters   |
| `setting_get` | `(kptr, klen, out_ptr, out_cap) -> i32` | the value's length, `-1` if unset, `-3` if `out_cap` is too small |
| `setting_set` | `(kptr, klen, vptr, vlen) -> i32`       | value up to 4 KB                                                  |
| `file_read`   | `(pptr, plen, out_ptr, out_cap) -> i32` | bytes read                                                        |
| `file_write`  | `(pptr, plen, dptr, dlen) -> i32`       | replaces the file safely; bytes written                           |

**Return codes:** `0` or more is success, `-1` invalid or not found, `-2` not allowed, `-3` too large.

**Events** arrive as JSON with a `type`:

```json
{"type":"init","profile_id":"emu"}
{"type":"session_start","profile_id":"emu","game_title":"…","game_path":"…"}
{"type":"session_stop","profile_id":"emu"}
{"type":"menu_action","id":"save"}
```

Register Quick Menu actions during `init` or `session_start`; they last until the game ends. Each event runs with a fixed instruction budget and memory is capped at 64 MB. A plugin that loops forever fails that event but stays loaded.

## API version 2: app plugins

Version 2 keeps everything in version 1 and adds **app plugins**, which run while Kouch is open rather than during a game. The built-in **Steam library** plugin is one.

* In `plugin.json`: `"api": 2` and `"scope": "app"`. `kouch_api` returns `2`.
* Events: `{"type":"init"}` and `{"type":"app_action","id":"sync"}`.
* Imports that return data answer `out_cap == 0` with the size they need: call once to size, then again.

| Import                 | Notes                                                                                                |
| ---------------------- | ---------------------------------------------------------------------------------------------------- |
| `app_action`           | `{"id":"sync","label":"Add games to Steam","description":"…"}`, registered during `init`             |
| `library_list`         | the game list: stable id, title, system, art, information. Games whose file is missing are left out. |
| `steam_user`           | the current Steam user and where Steam and Kouch are                                                 |
| `steam_shortcuts_read` | that user's Steam shortcuts file                                                                     |
| `steam_stage`          | propose a new shortcuts file and art. Kouch checks it and asks the user.                             |

`steam_stage` is strict: only Kouch's own shortcuts may change, every other entry must come back unchanged and in order, and art may only be for Kouch's shortcuts. The plugin never writes a file itself. Kouch does, after the user confirms.

## Build it

Any language that compiles to `wasm32-unknown-unknown` works: Rust, C, Zig, AssemblyScript and others. Don't use WASI; none of it is available.

## Test it

Copy the folder into Kouch's `plugins` folder (`%APPDATA%\kouch\plugins\` on Windows, `~/.config/kouch/plugins/` on Linux), open **Settings › Plugins**, turn it on and start a game. Anything the plugin logs goes to Kouch's log. See [Create a report](/troubleshooting/create-a-report.md) for where that is.


---

# 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/for-creators/creators/plugins.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.
