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:
- Pick a sample from the dropdown.
- Click ▶ Try Sample.
- 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:
| method | returns |
|---|---|
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) |
result | the success payload (varies per method) |
error | an Exception (or Forge::PackageManager::Error) |
on_complete | register 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:
- Fetches the manifest from
GET /api/packages/:name/versions/latest(or the version you specified). - Resolves dependencies and installs any that are not already present.
- Downloads the package zip from
GET /api/packages/:name/versions/:version/download. - Extracts it under
app/packages/:name/. - Appends a
requireline toapp/packages.rb. - 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_packages → Op
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_forge → Op
Refreshes the forge/ directory in place from the latest published Forge library, without touching:
api_key.rb— your credentialsapp/— your game codeapp/packages/andapp/packages.lock.json— your installed packagesmetadata/— DragonRuby metadataREADME.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 returns403 Forbiddenand the manager raisesForge::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
- Click Register in the nav.
- Fill in username, email, password, and password confirmation.
- 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'sapi_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'sapi_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:
| field | required | notes |
|---|---|---|
name | ✓ | Lowercase, starts with a letter, [a-z0-9_] only. Must be unique on the registry. |
version | ✓ | Semver (MAJOR.MINOR.PATCH), optional pre-release suffix (-rc1, .beta2). |
description | ✓ | One or two sentences. Shown on the card and detail page. |
dragonruby_version | ✓ | Version constraint string (e.g. >= 3.0, ~> 3.2). |
dependencies | Map of package_name → version_constraint. Installed automatically. | |
scripts | Class names of Forge::Script subclasses this package ships. | |
widgets | Class names of Forge::Widget subclasses this package ships. | |
assets | Paths inside the package that should be installed under app/packages/<name>/. | |
tags | Lowercase category words; drive the Filter by tag UI. | |
author | Filled in automatically when you publish via the website. |
Constraints on name and version
The website enforces:
namematches/\A[a-z][a-z0-9_]*\z/.versionmatches/\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
- Sign in.
- Click Publish in the nav (or go to
/packages/publish). - Either:
- Upload a ZIP containing your package directory — the form auto-fills from
manifest.jsonif 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.
- Upload a ZIP containing your package directory — the form auto-fills from
- 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
| Path | What it does |
|---|---|
/ | Browse all packages (search + tag filter) |
/packages/:name | Package detail page (version selector, code, samples) |
/packages/:name?version=1.2.0 | Detail page pinned to a specific version |
/packages/:name/files | File-tree Turbo Frame for the current/latest version |
/packages/:name/files/*path | File content Turbo Frame (syntax-highlighted) |
/packages/:name/samples/:name/embed/... | Browser embed serving the DragonRuby WASM runtime |
/packages/publish | Publish form (auth required) |
/new | New Game page — download the base library |
/new/download | The actual zip download (auto-generates an API key) |
/guide | This page |
/login · /register | Auth |
/logout | POST DELETE /logout |
/settings | Profile + API key management |
HTTP API (used by Forge.*)
| Method | Path | Notes |
|---|---|---|
| GET | /api/packages | List / search (?q=) |
| GET | /api/packages/:name | Package metadata |
| GET | /api/packages/:name/versions | All versions |
| GET | /api/packages/:name/versions/:version | Specific version |
| GET | /api/packages/:name/versions/:version/download | The package zip |
| GET | /api/packages/:name/versions/:version/files | File tree (JSON) |
| GET | /api/packages/:name/versions/:version/files/*path | File content |
| POST | /api/packages/publish | Publish; Authorization: Bearer <API_KEY> |
| POST | /api/auth/verify | Returns { valid, can_publish, username } |
| GET | /api/auth/me | Current user (authed) |
| GET | /api/forge/library | Forge base library zip (used by update_forge) |
Common errors
| Symptom | Cause | Fix |
|---|---|---|
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
- Visit the site, click New Game, download the zip.
- Extract, open in DragonRuby. The empty game runs.
- In the DragonRuby console:
Forge.add_package("starter_kit") Forge.add_package("health") Forge.add_package("ui_components") - Restart DragonRuby. The bootstrap
app/packages.rbnow requires the new packages. - 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: "...") - Your package is live — open the site, search for it, and click into the detail page to see the code viewer and Try Sample.