Configuration¶
Options¶
Feather:init(config) accepts the following options:
| Option | Type | Default | Description |
|---|---|---|---|
debug |
boolean |
false |
Enable or disable Feather entirely. |
host |
string |
"127.0.0.1" |
Desktop IP or hostname the game connects to. |
port |
number |
4004 |
Feather desktop WS server port. |
mode |
string |
"socket" |
"socket" for live WS, "disk" for log-file-only (no network). |
baseDir |
string |
"" |
Base directory path for file references and VS Code deeplinking. |
wrapPrint |
boolean |
false |
Wrap print() calls to send to Feather's log viewer. |
maxTempLogs |
number |
200 |
Max temporary logs stored before rotation. |
sampleRate |
number |
1 |
Seconds between push cycles (performance, observers, plugins). |
updateInterval |
number |
0.1 |
Interval between sending updates to clients. |
defaultObservers |
boolean |
false |
Register built-in variable watchers. |
errorWait |
number |
3 |
Seconds to wait for error delivery before showing LÖVE's handler. |
autoRegisterErrorHandler |
boolean |
false |
Replace LÖVE's errorhandler to capture errors. |
errorHandler |
function |
love.errorhandler |
Custom error handler to use. |
continueOnGameError |
boolean |
false |
When false, errors from wrapped game callbacks are delivered to Feather and then rethrown so the game crashes normally. Set true to log the error and keep the game loop running. |
gameErrorToast |
boolean |
true |
Show an in-game crash toast when continueOnGameError = true. |
plugins |
table |
{} |
List of plugin modules to load. |
captureScreenshot |
boolean |
false |
Capture screenshots on error. |
sessionName |
string |
"" |
Custom display name shown in desktop session tabs (e.g. "My RPG"). |
deviceId |
string |
auto-generated | Persistent device ID. Auto-generated and saved to disk if not set. |
writeToDisk |
boolean |
true |
Whether to write logs to .featherlog files. |
outputDir |
string |
"logs" |
Folder name (relative to the love save directory) where .featherlog files and log screenshots are written. |
capabilities |
string[] or "all" |
"all" |
Allowed plugin capability tokens. Plugins requesting undeclared capabilities log a warning at load time. Pass "all" to skip checking. |
retryInterval |
number |
2 |
Seconds between WebSocket reconnection attempts. |
connectTimeout |
number |
2 |
Seconds to wait for initial WS connection. |
appId |
string |
"" |
Desktop App ID that is allowed to send commands to this game. Copy from Feather → Settings → Security → Desktop App ID. Required unless __DANGEROUS_INSECURE_CONNECTION__ is set. |
__DANGEROUS_INSECURE_CONNECTION__ |
boolean |
false |
Explicit opt-in to skip the appId check and accept commands from any Feather desktop. Must be set to acknowledge the security risk (e.g. in a LAN jam build without a fixed App ID). |
debugger |
boolean or table |
false |
Enable the step debugger, or pass debugger options such as debugger.pauseOnError and debugger.hotReload. |
debugOverlay |
table or false |
enabled in debug | Small in-game “Feather debugger enabled” badge. Use enabled, visible, hideKey, touchToggle, and corner to customize it. Press F12 or double-tap the top-right corner to hide/show by default. |
assetPreview |
boolean |
true |
Enable the core Assets tab tracking and previews. Set to false to avoid hooking asset loaders and love.draw. |
binaryTextThreshold |
number |
4096 |
Observer, time-travel, debugger, and console text values longer than this many bytes are sent through the hybrid binary protocol instead of JSON. |
runtimeBudget |
table |
see below | Budget for non-critical Feather runtime work such as observer payloads, asset payloads, plugin pushes, profiler live uploads, binary drains, and GC steps. |
Warning
captureScreenshot can affect performance because it captures the current frame when errors are handled. Enable it only when you need visual error context.
Tip
continueOnGameError = true is useful for exploratory debugging because Feather keeps reporting repeated callback crashes and shows an in-game toast. Leave it off when you want normal LÖVE crash behavior.
You can also toggle this for the current run from Session → Keep Running After Callback Crashes.
Generated CLI Config¶
feather init defaults to CLI-managed mode. In that mode Feather creates feather.config.lua without patching game code, and feather run injects the runtime when you launch the game.
The generated CLI config includes:
return {
managed = "cli",
debug = true,
autoRegisterErrorHandler = true,
include = { "particle-system-playground", "shader-graph" },
capabilities = { "draw", "filesystem" },
}
Those default plugins are normal bundled plugins, not special cases. Other plugins are enabled by adding their IDs to include, either manually or with the CLI:
Plugins marked optIn = true or disabled = true in their manifest.lua only become active when included. This is how development-only tools such as Console and Hot Reload stay off unless you ask for them.
Runtime Budget And Panel Interest¶
Feather keeps connection, auth, errors, logs, debugger control, explicit profiler captures, and a low-rate performance heartbeat available while the game is connected. More expensive work such as observer serialization, asset payloads, plugin UI payloads, Time Travel, Session Replay, Runtime Snapshot, and creative preview sync is activated by opening the matching page or starting an explicit recording/preview workflow.
Non-critical work is spread across frames with runtimeBudget:
return {
runtimeBudget = {
maxFrameMs = 0.5,
maxMessagesPerFrame = 20,
maxSerializedBytesPerFrame = 32 * 1024,
},
}
| Field | Default | Description |
|---|---|---|
maxFrameMs |
0.5 |
Soft per-frame time budget for deferrable Feather work. |
maxMessagesPerFrame |
20 |
Maximum non-critical messages Feather should flush in one runtime frame. |
maxSerializedBytesPerFrame |
32 * 1024 |
Approximate serialized-byte budget for non-critical payload work per frame. |
Critical messages still send immediately under budget pressure: auth/hello/bye, fatal errors, command/action responses, debugger pause/resume/status, runtime suspend/resume, and explicit preview clear/hide actions.
Hot Reload¶
Hot reload is configured under debugger.hotReload, but the command handler only exists when the opt-in hot-reload plugin is installed and included:
return {
include = { "hot-reload" },
debugger = {
enabled = true,
hotReload = {
enabled = true,
allow = { "game.player", "game.systems.*" },
deny = { "main", "conf", "feather.*" },
persistToDisk = false,
clearOnBoot = false,
requireLocalNetwork = true,
},
},
}
The CLI can write the same shape for you:
feather init path/to/my-game --plugins hot-reload --hot-reload-allow game.player,game.systems.combat --yes
feather config hot-reload --allow game.player,game.systems.combat --dir path/to/my-game
See Hot Reload for the full workflow.
Warning
Hot reload is remote code execution for development. Leave the hot-reload plugin excluded unless you are actively using it.
Caution
Broad allowlists such as allow = { "game.*" } let Feather replace many modules at runtime. Start with exact module names and keep persistToDisk = false unless you explicitly need persistent mobile patches.
Security¶
Feather uses a nonce-based challenge-response handshake to authenticate each connection before any game data is exchanged.
The CLI can audit these settings:
--production fails on unsafe release settings. --security --json emits a machine-readable security report and redacts sensitive values such as apiKey.
How it works¶
- When the game connects, the desktop immediately sends a one-time challenge nonce.
- The game responds with its configured
appIdand the nonce. - The desktop validates both. On success it sends
auth:okand the game begins pushing data. On failure it sendsauth:failand closes the connection.
No game data (feather:hello, observers, logs, etc.) is sent until the handshake completes.
Setting up appId¶
Copy your App ID from Feather → Settings → Security → Desktop App ID and add it to your config:
The same appId must be set in both the desktop settings and your game config. If they don't match, the connection is rejected and the game logs:
Insecure mode¶
For quick local prototyping or LAN jam builds where you don't want to manage a fixed App ID:
The desktop will accept the connection and show an insecure badge on the session tab. The field name is intentionally verbose — it must be set explicitly to acknowledge the risk.
Caution
Insecure mode allows any Feather desktop on the network to send commands to your game, including triggering the console plugin if installed. Do not ship production builds with insecure mode enabled.
Console API key¶
The Console plugin can evaluate Lua inside the running game. If console is included, configure a strong apiKey and match it in Feather desktop Settings:
Doctor reports whether the key is configured or weak, but does not print the key value.
MCP Access¶
Feather can expose live desktop sessions to AI clients through a local Model Context Protocol server:
feather mcp
feather mcp --transport http
feather mcp setup --client codex
feather mcp setup --client claude
Enable Feather → Settings → Security → MCP Access first. The desktop bridge binds to 127.0.0.1:4005, is disabled by default, and requires a generated bearer token. The CLI reads that token from ~/.feather/mcp.json, or from FEATHER_MCP_TOKEN / --token.
Use feather mcp setup --client codex to install the Feather MCP server entry into ~/.codex/config.toml. Use feather mcp setup --client claude to install the Claude Code user entry into ~/.claude.json, or add --scope project to write a project .mcp.json. Restart the client after setup so the tools are loaded.
MCP resources expose sanitized JSON snapshots such as sessions, config, logs, performance, debugger state, plugin catalog/live payloads, assets, observers, Shader Graph, Particles Playground, and Texture Lab. MCP tools can control runtime/debugger/plugin workflows, Shader Graph compile/preview/import/export, Particle Playground authoring/export actions, and Texture Lab recipe/generation actions. Console eval still works only when the existing Console gates are satisfied (console included, evalEnabled = true, and matching apiKey). See MCP for setup, resource/tool lists, and troubleshooting.
References: MCP specification, MCP architecture, and MCP transports.
Connecting¶
Feather uses a push-based WebSocket architecture. The desktop app runs a WebSocket server (port 4004 by default), and your game connects to it as a client.
- Start the Feather desktop app from the releases page.
- Run your game with Feather enabled — you'll see:
The game automatically reconnects if the desktop app is restarted.
Mobile & Remote Devices¶
Android (USB via ADB reverse)¶
Forward the device's localhost:4004 to your computer:
Then use the default config — the game connects to 127.0.0.1:4004, which ADB routes to your computer:
Android / iOS (Wi-Fi)¶
Point host to your computer's local IP:
Tip
Open Feather → Settings → Mobile Connection. Your local IP is auto-detected with a copyable ws:// connection string and ready-to-paste Lua snippet.
Offline / disk mode¶
Skip WebSocket entirely and write only log files — useful when there's no shared network:
Transfer the .featherlog file from the device and open it in the Feather app via Open Log File.