Login

WarpEngine

WarpEngine is a standalone, mountable Rails engine that turns any Rails application into a retro software catalog: catalog models, a CI-pipeline-callable release updater, a public read-only JSON API and optional ActiveAdmin resources that plug into the host's existing admin. The Teletype Games public site is its reference host, but the engine carries no TTG-specific code — any Rails app can mount it.

Repositories

Repository Role
tools/warp_engine The product. Consume it from here as a git-sourced gem, or from the Forgejo rubygems registry on tagged releases (warp_engine-vX.Y.Z). Read-only mirror — do not push or open PRs here.
tools/teletypegames Development home (monorepo); CI republishes the mirror on every change.

Platform build tooling

The per-platform build-tools repos are the producer side of the updater contract: each one ships CI/CD templates (an example project Makefile, an example Woodpecker pipeline and, where needed, a builder image Dockerfile) that build the game, name the artifacts by WarpEngine's convention, upload them to the artifact drop area and call the /update endpoint with the matching platform parameter.

Repository Platform
tools/tic80-tools TIC-80
tools/ebitengine-tools Ebitengine
tools/love-tools LÖVE
tools/c64-tools Commodore 64
tools/godot-tools Godot
tools/bevy-tools Bevy
tools/phaser-tools Phaser
tic80pro-dockerfile TIC-80 Pro builder image (used by the TIC-80 pipeline)

What You Get

  • Catalog domain: Software, Release, ReleaseAsset, ExternalLink, PlatformLink, Image, SoftwareImage, Download models with soft-delete semantics and download statistics.
  • CI-callable updater: the build pipeline drops artifacts into a directory and calls one endpoint; WarpEngine extracts archives, parses metadata and upserts the catalog records. Platforms supported out of the box: TIC-80, Ebitengine, LÖVE, C64, Godot, Bevy, Phaser.
  • Public JSON API: catalog listing, highlighted title, per-platform build matrix, image serving, download tracking, and a static file server for web-playable builds.
  • Optional admin: if the host runs ActiveAdmin, WarpEngine contributes ready-made resources (catalog editor with nested release/asset forms, image library with orphan cleanup, file manager with picker mode, download stats). Without ActiveAdmin the engine runs headless (API + updater only).

Public Endpoints

Endpoint Purpose
GET /api/software Full catalog with releases, assets, links and download counts.
GET /api/software/highlighted The currently highlighted title.
GET /api/builds Global build matrix: expected asset kinds per platform.
GET /api/softwares/:name/builds Actual vs. missing build assets per release.
GET /api/image/:id Serves catalog images.
GET /api/download?path= Serves a build artifact and logs a download record.
GET /file/*path Serves static build output (web-playable games, docs).
GET /update The updater — see below. Protected by a shared secret.

The Updater Contract

Publishing a release from CI is two steps:

  1. Upload: build artifacts are copied into the directory configured as file_container_path, named by convention: <name>-<version>.metadata.json, <name>-<version>.html.zip, <name>-<version>-win-x64.zip, <name>-<version>.tic, etc. Each platform declares which asset kinds it expects — see GET /api/builds.
  2. Update call: GET /update?platform=<platform>&name=<name>&version=<version> with the secret in the X-Update-Secret header (preferred) or ?secret=.

The updater extracts archives, parses metadata (JSON, or the Lua comment header for TIC-80), and upserts the Software, ExternalLink, Release and ReleaseAsset records in one transaction. Soft-deleted records are resurrected on re-ingest.

You don't have to build this pipeline from scratch: the platform build tooling repos provide ready-made Makefile and Woodpecker templates that implement both steps for every supported platform.

Security note: if no update_secret is configured, the /update endpoint rejects every request (it does not fall back to an empty secret).

Installing Into a Host

RUBY
# Gemfile — from git:
gem "warp_engine", git: "https://git.teletypegames.org/tools/warp_engine.git"

# ...or from the Forgejo rubygems registry (tagged releases):
source "https://git.teletypegames.org/api/packages/tools/rubygems" do
  gem "warp_engine"
end
SH
rails g warp_engine:install   # initializer + create_warp_engine_tables migration
rails db:migrate
RUBY
# config/routes.rb — keep it the last entry so the host's own routes win
mount WarpEngine::Engine => "/"

Configuration

RUBY
# config/initializers/warp_engine.rb
Rails.application.config.to_prepare do
  WarpEngine.configure do |c|
    c.file_container_path  = ENV.fetch("FILE_CONTAINER_PATH", "/softwares")
    c.image_container_path = ENV.fetch("IMAGE_CONTAINER_PATH", "/images")
    c.update_secret        = ENV["UPDATE_SECRET"]
    # If host models reference catalog images, register them so the admin
    # Images page counts them as "in use":
    c.image_owners = [
      {
        label: "member",
        image_ids: -> { Member.where.not(image_id: nil).distinct.pluck(:image_id) },
        usage_label: ->(image) { "member" if Member.where(image_id: image.id).exists? }
      }
    ]
  end
end

Admin Integration

The host owns the single ActiveAdmin instance — authentication (Devise), theme, assets and the /admin routes. WarpEngine only appends its resource files to ActiveAdmin.application.load_paths, so the engine's resources appear in the host's menu as if they were its own. Two things to copy into a new host:

  • the small file-picker JS for release-asset path inputs (an iframe pointing at /admin/files?picker=1&field=<dom_id>) in the host's active_admin.js;
  • if the host generates apipie docs, add "#{WarpEngine::Engine.root}/app/controllers/**/*.rb" to its api_controllers_matcher.

The one place where the catalog needs host knowledge — "which host models use catalog images?" — is inverted into the image_owners config hook shown above.

Behavioral Notes

  • Every model is soft-deleted (default_scope { where(deleted_at: nil) }).
  • The JSON shape is stable and intentionally bug-compatible with the project's former Go backend (Go zero-time timestamps, camelCase keys, legacy flat path fields).
  • Model extension points: ActiveSupport.on_load(:warp_engine_<model>) hooks.

Testing

The engine ships an RSpec suite running against a bundled dummy app (MySQL):

SH
bundle install
bundle exec rake app:db:prepare RAILS_ENV=test
bundle exec rspec