DragonRuby Forge — User Guide

Forge is a package registry for the DragonRuby Game Toolkit. Discover, install, and share reusable game components — no separate command-line tool, no git submodule. Everything happens through the website and from inside your game.

This guide covers three flows: using the website, interacting with packages in DragonRuby, and publishing your own packages.

1. Using the website

From the top nav you always have access to New Game (download a starter project), Publish (signed in), and Settings (signed in), plus Login and Register.

1.1 Browse the registry

The home page (/) lists every published package as a card showing the name, latest version, author, description, and tags.

  • Search — type into the search box and hit Search. Matches are checked against the package name and description.
  • Filter by tag — click any tag pill (e.g. physics, gameplay, ui) to narrow the list. Click All to clear.
  • Clear filters — when a filter is active, a Clear link appears next to the search button.

The list is ordered by most recently created first.

1.2 Package detail page

Click any package card to open its detail page (/packages/:name). The page has four main areas:

Header

  • Name and a version selector dropdown — every released version is listed, with (latest) next to the current one. Picking a version reloads the page at that version.
  • Description and author (by <username>).

Details panel

  • DragonRuby Version — required DR version range (e.g. >= 3.0).
  • Dependencies — other packages this one needs, each with its version constraint. Click any to navigate to that package.
  • Scripts — Ruby class names the package exposes (e.g. HealthScript, LdtkLoaderScript).
  • Tags — clickable, navigate to the filtered list.

Installation panel

A single one-line command:

Forge.add_package("health")

Hit Copy to grab the snippet. Paste it into the DragonRuby console, into main.rb, or any tick that runs only on the first frame. See section 2 for the full flow.

Try Sample

If the package ships with one or more samples (named example projects under samples/<name>/ in the package), this panel appears:

  1. Pick a sample from the dropdown.
  2. Click ▶ Try Sample.
  3. A modal opens with the DragonRuby WASM runtime running that sample in your browser — full game, no install needed. Close with × when done.

The browser embed synthesizes a minimal app/main.rb plus the Forge runtime stubs and serves the sample's real assets (sprites, sounds, data files) straight from the package.

Source code browser

A two-pane code viewer:

  • Left — collapsible file tree (folders like scripts/, widgets/, assets/, plus any top-level files). Click a folder arrow to expand/collapse.
  • Right — the selected file, syntax-highlighted. Default opened file is manifest.json. Files load via Turbo Frames, so navigation is instant.

The viewer always reflects the version selected in the header. Switch versions to inspect a historical release.

Version history

When a package has more than one version, a Version History section appears at the bottom, listing every release with its publish date and a View link.

1.3 Account & settings

Register

Click Register in the nav. Provide a username, email, password, and password confirmation. On success you're signed in and redirected home.

Login / Logout

Login uses your email and password. The header swaps to Hello, <username> plus a Logout button.

Settings (/settings)

The hub for everything tied to your account:

  • Profile — change username or email. Both must be unique.
  • API keys — one row per key, showing the key name, download count, publish count, and creation date. Actions:
    • Create new key — manually mint a new key tied to your account, useful for a new project.
    • Claim key — paste an fk_live_... key from an anonymous download. It gets linked to your account and publishing is enabled for any project using it.
    • Regenerate — issue a new key string for an existing key row (useful if the original leaked; the old one stops working immediately).
    • Delete — remove a key entirely. A project using that key loses publishing and is treated as anonymous.

You can have multiple keys per account — one per project is the recommended pattern.

1.4 The “New Game” page

/new is the entry point for starting a new project. There's a single big button: Download Forge Base Library.

  • If you're signed in, the API key embedded in the download is linked to your account immediately — you can publish from that project as soon as you write code.
  • If you're not signed in, an anonymous user is created on the fly and the key is tied to that anonymous user. The project can still install and use packages, but publishing is disabled until you register (or sign in) and claim the key from /settings.

The downloaded zip is a self-contained DragonRuby project — no gem install, no CLI, no git submodule. Extract it, drag the folder onto your DragonRuby executable, and run.

2. Interacting with packages in DragonRuby

Every project you download from the New Game page is a vanilla DragonRuby project with one extra thing: a forge/ directory containing the Forge base library plus an in-game package manager. You never edit forge/.

2.1 What's in the project

After extracting forge-project.zip, your folder looks like this:

my-game/
  app/
    main.rb             # your game entry point
    forge.rb            # requires the Forge base library
    base/               # Process, Entity, Script, Widget, etc.
    packages/           # installed packages live here
    packages.rb         # auto-generated, requires all installed packages
    packages.lock.json  # auto-generated, records installed versions
  forge/                # base library (do not edit; update with Forge.update_forge)
  metadata/             # DragonRuby metadata (game_metadata.txt, icon.png)
  api_key.rb            # your Forge API key (gitignored)
  api_key.rb.example    # template, safe to commit
  README.md

main.rb is the standard DragonRuby entry point:

require "app/forge"
require "app/packages"

def tick(args)
  Forge.tick(args)
end

Forge.tick(args) advances every Process, Entity, and registered script. You write your game in app/; forge/ is the engine.

2.2 The package manager API

All package operations are methods on the Forge module. Methods that hit the network return an Op result handle — on DragonRuby GTK the HTTP requests are async, so you use a callback. On plain MRI (tests, server-side calls) the same code runs synchronously and the callback fires before the method returns.

# Synchronous style (works everywhere; on DR the Op is already complete)
Forge.add_package("health")
Forge.list_installed
Forge.remove_package("health")

# Callback style (recommended for DragonRuby — be explicit about async)
Forge.add_package("health") do |op|
  if op.failed?
    puts "Error: #{op.error.message}"
  else
    puts "Installed #{op.result[:name]}@#{op.result[:version]}"
  end
end

Op exposes:

methodreturns
complete?true once the request has finished (success or fail)
succeeded?true if it finished without an error
failed?true if it errored (op.error is set)
resultthe success payload (varies per method)
erroran Exception (or Forge::PackageManager::Error)
on_completeregister a callback to be invoked on completion

Forge.add_package(name, version: nil)Op

Installs a package and all of its dependencies.

# latest version
Forge.add_package("health")

# specific version
Forge.add_package("health", version: "1.2.0")

Behind the scenes:

  1. Fetches the manifest from GET /api/packages/:name/versions/latest (or the version you specified).
  2. Resolves dependencies and installs any that are not already present.
  3. Downloads the package zip from GET /api/packages/:name/versions/:version/download.
  4. Extracts it under app/packages/:name/.
  5. Appends a require line to app/packages.rb.
  6. Adds an entry to app/packages.lock.json.

The lock file makes installs reproducible — commit it alongside app/packages/ so anyone who clones your game gets the same package versions.

Forge.remove_package(name)

Removes a package from disk and from the lock file. Raises Forge::PackageManager::PackageNotFound if it isn't installed. Does not re-require anything; restart DragonRuby (or your game) to drop the loaded code from memory.

Forge.update_packagesOp

Walks every package in packages.lock.json, asks the registry for the latest version, and upgrades any that are behind. Dependencies are respected — a package is only upgraded if its new version's constraints are still satisfiable.

Forge.list_installed

Returns an array of { name:, version:, manifest: } hashes from the lock file. Synchronous; doesn't touch the network.

Forge.search(query)Op

Hits GET /api/packages?q=<query> and returns the matching packages (or [] on an empty result).

Forge.update_forgeOp

Refreshes the forge/ directory in place from the latest published Forge library, without touching:

  • api_key.rb — your credentials
  • app/ — your game code
  • app/packages/ and app/packages.lock.json — your installed packages
  • metadata/ — DragonRuby metadata
  • README.md — your project readme

Run it when you want the latest engine, then restart DragonRuby.

2.3 Where packages end up

After Forge.add_package("health"):

my-game/
  app/
    packages/
      health/
        manifest.json
        scripts/
          health_script.rb
        widgets/      # if any
        assets/       # if any
    packages.rb       # now includes:
                      #   require "app/packages/health/scripts/health_script"
    packages.lock.json

In your game code, you require the package's scripts the way the bootstrap file does, or just use them directly (the autoloaded app/packages.rb makes every class in the package available globally on next start).

2.4 API key behavior

The key in api_key.rb is automatically generated when you download a project from the website. Every API call sends it as Authorization: Bearer <key>.

  • Anonymous keys (downloaded without signing in) can install and update packages but cannot publish. If you try Forge.publish_package(...) with an anonymous key, the server returns 403 Forbidden and the manager raises Forge::PackageManager::AuthenticationRequired.
  • Registered keys (claimed via /settings, or freshly created by a signed-in user) can do everything.

If you've been using a project anonymously and want to publish from it: register on the site, then go to Settings → Claim key and paste your fk_live_... key string. The key gets linked to your account and publishing unlocks.

api_key.rb is in .gitignore by default. Don't commit it. If you need to back up the project, copy the key out separately — losing the key is recoverable (regenerate it under Settings), but accidentally publishing it is not.

3. Publishing your own packages

You can publish from two places: the website (recommended for first-time setup) or from inside your game (great once you have a registered key).

3.1 Create your account

  1. Click Register in the nav.
  2. Fill in username, email, password, and password confirmation.
  3. You're now signed in.

3.2 Make sure your project has a publishing-capable key

  • Downloaded after signing in — your key already publishes. Skip to 3.3.
  • Downloaded anonymously — go to Settings → Claim key, paste the fk_live_... value from your project's api_key.rb, and submit.
  • Want a fresh key — go to Settings → Create new key, give it a name, and copy the generated fk_live_... into your project's api_key.rb.

Verify from inside the game with:

Forge.can_publish? do |op|
  if op.succeeded?
    puts op.result ? "Ready to publish" : "Anonymous — register at https://forge.jleb.dev to publish"
  end
end

3.3 Package structure

A package is a directory with a manifest.json and three optional folders:

my_cool_script/
  manifest.json
  scripts/
    my_cool_script.rb
  widgets/                # optional
    my_cool_widget.rb
  assets/                 # optional
    sprites/foo.png
    sounds/blip.ogg
  samples/                # optional — example projects for "Try Sample"
    basic_demo/
      main.rb
      sprites/

manifest.json

{
  "name": "my_cool_script",
  "version": "1.0.0",
  "description": "A short, one-sentence summary of what this does.",
  "dragonruby_version": ">= 3.0",
  "dependencies": {
    "physics": ">= 1.0.0"
  },
  "scripts": ["MyCoolScript"],
  "widgets": ["MyCoolWidget"],
  "assets": ["sprites/foo.png"],
  "tags": ["gameplay", "rpg"],
  "author": "your_username"
}

Field reference:

fieldrequirednotes
nameLowercase, starts with a letter, [a-z0-9_] only. Must be unique on the registry.
versionSemver (MAJOR.MINOR.PATCH), optional pre-release suffix (-rc1, .beta2).
descriptionOne or two sentences. Shown on the card and detail page.
dragonruby_versionVersion constraint string (e.g. >= 3.0, ~> 3.2).
dependenciesMap of package_name → version_constraint. Installed automatically.
scriptsClass names of Forge::Script subclasses this package ships.
widgetsClass names of Forge::Widget subclasses this package ships.
assetsPaths inside the package that should be installed under app/packages/<name>/.
tagsLowercase category words; drive the Filter by tag UI.
authorFilled in automatically when you publish via the website.

Constraints on name and version

The website enforces:

  • name matches /\A[a-z][a-z0-9_]*\z/.
  • version matches /\A\d+\.\d+\.\d+([.-]\w+)?\z/.
  • You cannot publish a version of a package that already exists — to release a new build, bump the version number.

3.4 Publishing from the website

  1. Sign in.
  2. Click Publish in the nav (or go to /packages/publish).
  3. Either:
    • Upload a ZIP containing your package directory — the form auto-fills from manifest.json if present, or from the file structure (scripts/*.rb, widgets/*.rb, assets/*).
    • Fill the form manually — name, version, description, DragonRuby version, dependencies, scripts, widgets, tags, assets, and any sample names.
  4. Click Publish Package.

On success you're redirected to the new package's detail page. The package is immediately visible in the public listing.

ZIP upload format: a single top-level folder matching the package name, containing the same layout described in 3.3. The validator will check that the folder name matches the declared name and that the version matches the declared version.

3.5 Publishing from inside the game

If your project's API key has publishing rights, push directly from the DragonRuby console or your code:

Forge.publish_package(
  name: "my_cool_script",
  version: "1.0.0",
  description: "A short summary of what this does.",
  scripts: ["MyCoolScript"],
  widgets: ["MyCoolWidget"],
  assets: ["sprites/foo.png"],
  dependencies: { "physics" => ">= 1.0.0" },
  tags: ["gameplay", "rpg"]
) do |op|
  if op.succeeded?
    puts "Published: #{op.result["url"]}"
  else
    puts "Error: #{op.error.message}"
  end
end

The manager verifies your key first (and bails with AuthenticationRequired if it's anonymous), then POSTs the metadata to /api/packages/publish. The server creates a new package or appends a new version to an existing one.

To publish a new version of a package you already own, call publish_package again with the same name and a higher version.

3.6 Samples (the in-browser preview)

If your package includes a runnable example, put it under samples/<demo_name>/ with the same layout as a real Forge project (main.rb, sprites/, sounds/, etc.). List the names in the publish form's Sample Games field (or the samples array when publishing from the game).

The detail page will then show the Try Sample panel, and the browser embed will run the sample live using the DragonRuby WASM runtime. Nothing extra to wire up — the embed synthesizes a tiny main.rb and the Forge runtime stubs, and serves the rest of the sample's files straight from the package zip.

3.7 Versioning rules of thumb

  • Patch (1.0.0 → 1.0.1) for bug fixes that don't change the public API.
  • Minor (1.0.0 → 1.1.0) for new scripts, new widgets, or backward-compatible behavior changes.
  • Major (1.0.0 → 2.0.0) when you rename or remove a class, change a script's lifecycle signature, or break dependency requirements.

You cannot unpublish or delete a version once it's live — the registry keeps the full history. To "fix" a bad release, publish a new version with a higher number. This is on purpose: it makes packages.lock.json reproducible forever.

Reference

Routes summary

PathWhat it does
/Browse all packages (search + tag filter)
/packages/:namePackage detail page (version selector, code, samples)
/packages/:name?version=1.2.0Detail page pinned to a specific version
/packages/:name/filesFile-tree Turbo Frame for the current/latest version
/packages/:name/files/*pathFile content Turbo Frame (syntax-highlighted)
/packages/:name/samples/:name/embed/...Browser embed serving the DragonRuby WASM runtime
/packages/publishPublish form (auth required)
/newNew Game page — download the base library
/new/downloadThe actual zip download (auto-generates an API key)
/guideThis page
/login · /registerAuth
/logoutPOST DELETE /logout
/settingsProfile + API key management

HTTP API (used by Forge.*)

MethodPathNotes
GET/api/packagesList / search (?q=)
GET/api/packages/:namePackage metadata
GET/api/packages/:name/versionsAll versions
GET/api/packages/:name/versions/:versionSpecific version
GET/api/packages/:name/versions/:version/downloadThe package zip
GET/api/packages/:name/versions/:version/filesFile tree (JSON)
GET/api/packages/:name/versions/:version/files/*pathFile content
POST/api/packages/publishPublish; Authorization: Bearer <API_KEY>
POST/api/auth/verifyReturns { valid, can_publish, username }
GET/api/auth/meCurrent user (authed)
GET/api/forge/libraryForge base library zip (used by update_forge)

Common errors

SymptomCauseFix
Anonymous API keys cannot publish. Your project has an anonymous key. Register, then claim the key at /settings.
Version X.Y.Z of this package already exists You tried to re-publish the same version. Bump the version (semver).
Invalid package archive: ... ZIP's top-level folder name doesn't match the declared name/version. Re-zip from the package directory so the top-level folder name matches.
Package not installed: <name> remove_package called for a package that's not in the lock file. Run Forge.list_installed to see what's actually installed.
Browser embed shows a black/blank canvas Sample is missing a main.rb, or files are not under samples/<name>/. Make sure samples/<name>/main.rb exists, and samples is listed in the publish form.
404 when installing a known package Typo in the name, or the package was unpublished (rare — versions are kept). Search the registry to confirm the exact name.

Quick start — 5 minutes

  1. Visit the site, click New Game, download the zip.
  2. Extract, open in DragonRuby. The empty game runs.
  3. In the DragonRuby console:
    Forge.add_package("starter_kit")
    Forge.add_package("health")
    Forge.add_package("ui_components")
  4. Restart DragonRuby. The bootstrap app/packages.rb now requires the new packages.
  5. To publish your own: register on the site, claim the key, then in the console:
    Forge.publish_package(name: "my_script", version: "1.0.0", description: "...")
  6. Your package is live — open the site, search for it, and click into the detail page to see the code viewer and Try Sample.