butler Documentation

repository·master·Indexed 21 days ago

https://github.com/itchio/butler

The official command-line toolset for itch.io, used by creators for uploading builds and by the itch app for patching and filesystem operations. Includes documentation for the butler daemon (butlerd), covering TCP and Stdio transports, JSON-RPC 2.0 communication, profile management, game fetching, and lifecycle control via Meta.Flow and Meta.Shutdown.

Tokens
74K
Snippets
281
Records
414
Agent score
74%

What's inside butler

  1. Overview of butlerd (butler daemon)

    master

    butlerd

    butlerd is a JSON-RPC 2.0 service designed to handle butler tasks. It is used to manage either long-running tasks (referred to as operations) or individual one-off requests.

    It supports two transport mechanisms:

    • TCP: For network-based communication.
    • stdio: For communication via standard input/output.
  2. Overview of butler

    master

    butler is the official itch.io command-line tool designed for managing game builds. It provides a reliable way to:

    • Upload builds: Quickly and reliably push game builds to itch.io.
    • Manage patches: Generate patches and apply them offline.

    While butler is a CLI tool, users who prefer a graphical interface can use the itch app (v26.12.0 or later), which includes a built-in interface for pushing builds, viewing change previews, and managing builds via the 'Builds' page in the sidebar.

  3. How channel names affect platform tagging

    master

    Butler uses the channel name to automatically tag builds with platform metadata. While you can manually override these tags on the itch.io Edit game page, using specific keywords in your channel name helps automate the process.

    Platform Keywords:

    • win or windows $\rightarrow$ Windows executable
    • linux $\rightarrow$ Linux executable
    • mac or osx $\rightarrow$ Mac executable
    • android $\rightarrow$ Android application

    Conventions:

    • Use kebab-case (lowercase with dashes).
    • Channels can represent multiple platforms (e.g., win-linux-mac-stable).
    • Limitation: There is currently no way to tag channels with architecture (e.g., 32-bit vs 64-bit) via naming.
  4. Monitor Publish.Push progress and lifecycle

    master

    When performing a Publish.Push, butlerd emits several notifications to allow clients to track the operation's lifecycle and progress.

    Lifecycle Notifications

    • Publish.Push.BuildAssigned: Emitted once as soon as the worker obtains a build ID from the itch.io API (after CreateBuild succeeds but before data flows). Use this to associate your in-flight push with the server-side build ID.
    • Publish.Push.BuildFailed: Emitted if the push errors out after a build was assigned and the worker successfully marked the build as failed on the server. This allows updating local views to "failed" without polling.
    • Publish.Push.Progress: Periodic updates emitted while the push is in flight.

    Progress Payload Fields

    FieldTypeDescription
    progressnumber0..1; conservative estimate based on uploaded vs source size
    etanumberEstimated seconds remaining (0 if unknown). Refers to the upload phase
    bpsnumberUpload bytes per second (wire throughput, not disk read speed)
    readBytesnumberBytes read from the source container so far while computing the patch
    totalBytesnumberTotal bytes in the source container
    uploadedBytesnumberBytes of the patch uploaded to itch.io so far
    patchBytesnumberCompressed patch size produced so far. Equals uploadedBytes once upload catches up
  5. Check for butler updates

    master
    Butler automatically performs version checks in the background when you execute network-dependent commands such as butler push or butler status. If a newer version is available, a notice will be displayed in your terminal.
  6. Understand the Manifest data format

    master

    A Manifest describes prerequisites (dependencies) and actions that can be taken while launching a game.

    struct Manifest {
      actions: Actions; // List of options to give the user when launching a game
      prereqs?: Prereq[]; // Libraries or frameworks that must be installed prior to launching
    }
  7. Important butlerd integration details

    master

    When building a launcher, keep these technical constraints in mind:

    • Notification Lifetimes: Notifications can arrive after a call has resolved. Do not immediately tear down conversation handlers after receiving a response; continue draining messages for a short period. Notification handler lifetime is tied to the surrounding call, not the response message.
    • Error Handling: Errors return structured data. A failed call includes a data.apiError object containing a statusCode and a messages array. These messages are human-readable and intended to be shown directly in your UI.
    • Single-Tenant Daemon: butlerd is single-tenant and expects exactly one client. If your application has multiple windows or processes, multiplex them through a single connection/daemon. Never spawn multiple daemons against the same --dbpath.
    • Cooperative Cancellation: Long-running operations accept a client-generated id. To abort an operation, call the corresponding *.Cancel method using that same id.
  8. Understand Caves (installed-game records)

    master

    In butlerd, every installation is represented as a cave. A cave is a unique record of an installation.

    Cave Properties:

    • caveId: A unique UUID.
    • Metadata: The game and upload it was installed from, build metadata (for wharf-channels), install location, and folder path.
    • Usage Stats: Size on disk, last-played timestamp, and total seconds run.

    One game can have multiple caves (e.g., different versions or different install locations). Most launchers use Fetch.Caves filtered by gameId to determine if a game is installed and show its status.

  9. Understand Butler's Bundle Ownership Model

    master

    Butler handles large bundles (containing thousands of games) using a two-stage ownership model to prevent database bloat. Instead of creating a DownloadKey for every game at the time of purchase, ownership is managed via a BundleKey.

    Key Concepts:

    • Non-materialized Ownership: A user owns a BundleKey per bundle purchase. The individual games inside the bundle are not yet DownloadKey rows in the database.
    • Lazy Materialization: A real DownloadKey is only created (minted) when the user actually attempts to install a specific game from that bundle. This is triggered by the ClaimBundleGame operation.
    • Ownership Inference: Butler can determine a user owns a game via a bundle even if no local DownloadKey exists, allowing the UI to show "Install" instead of "Buy now."

    Product Semantics:

    • Bundle-contained games do not appear in the standard "Owned" library page until they are materialized via installation.
    • Bundle contents are browsed through a separate bundle library surface (owned bundles list $\rightarrow$ bundle detail page).
  10. Handle requests, notifications, and server-to-client requests

    master

    A robust butlerd client must handle three types of JSON-RPC messages:

    1. Requests/Responses: Standard client-to-server calls. Send a request with an id and await the response with the same id.
    2. Notifications: Server-to-client messages (no id) used to report progress on long-running operations.
    3. Server-to-Client Requests: During an active "conversation" (e.g., an interactive prompt like an upload picker or license acceptance), butlerd may send a request to the client. The client must be able to receive these requests and send back a response object.

    Implementation Pattern: Model your client with three components:

    • A way to await responses by id.
    • A dispatcher for incoming notifications (keyed by method).
    • A dispatcher for incoming server-to-client requests (keyed by method) that returns a response.
  11. How to handle bundle ownership in the renderer

    master

    To maintain performance and avoid massive memory footprints, the renderer should follow these patterns for bundle ownership:

    1. Avoid Fetch.Commons for bundles: Do not add the entire bundle ownership set to the global Commons object. Commons should remain focused on small, global state like materialized download keys and caves.
    2. Use Fetch.GameOwnership for game pages: On direct game pages, layer Fetch.GameOwnership on top of existing game status. This allows a bundle-owned game to show an 'Install' button without requiring the entire library to be loaded into memory.
    3. Use Fetch.BundleGames for bundle pages: When viewing a bundle detail page, use the paginated Fetch.BundleGames endpoint to show its contents.
    4. Handle Staleness: If Fetch.GameOwnership returns Stale: true, the renderer should trigger a background sync via Fetch.ProfileBundleOwnerships(fresh: true). This should be debounced and avoided on high-frequency UI elements like search inputs or list hovers.
  12. Understand the butlerd Launch Message Flow

    master

    When the itch app calls Launch, butlerd orchestrates a multi-phase flow including target selection, prerequisites, sandbox setup, and game execution. The communication between the client (itch app) and butlerd uses two types of messages:

    1. Requests: The client must respond to these (e.g., accepting a license or picking a launch target).
    2. Notifications: These are informational updates (e.g., progress updates or process exit notifications) and do not require a response.

    Understanding this flow is essential for implementing a client that can correctly handle game launches, license agreements, and prerequisite installations managed by butlerd.