Docs · Core Concepts

Architecture Tour

The Rails 8 stack: SQLite, Solid Queue, Hotwire, and the adapter+fake pattern.

One Shot is a Rails 8 app that deliberately runs on as few moving parts as possible. There's no Postgres, no Redis, and no separate JavaScript build in the default setup, and that isn't a half-finished state; it's the intended shape for a kit meant to run on a single small server.

The stack

  • SQLite is the database, in development, test, and production. Rails 8's SQLite adapter is production-ready, and bin/setup/bin/dev need nothing installed or running beyond Ruby to get a working database. See Production Database and Storage for how this holds up under real traffic.
  • Solid Queue, Solid Cache, and Solid Cable replace what Redis usually does: background jobs, caching, and Action Cable's pub/sub, all backed by the same SQLite database instead of a separate service. SomeJob.perform_later just works, with no Redis to provision.
  • Propshaft serves assets, and Importmap serves JavaScript straight from app/javascript/controllers/ with no bundler, no npm install, and no build step. A new Stimulus controller is auto-registered the moment you add the file.
  • Hotwire (Turbo + Stimulus) is the whole frontend approach. Interactivity comes from Turbo Frames/Streams and small Stimulus controllers, not a separate JavaScript SPA talking to a JSON API. This is also what makes the same views work inside the native mobile shells with almost no extra code; see Mobile Overview.
  • Pundit handles authorization, OmniAuth handles Google/Apple sign-in, and passwordless magic-code sign-in is custom, purpose-built for this kit rather than a general-purpose gem.
  • Stripe is the payment processor when you provide a key; without one, a Local billing fake stands in. This adapter-plus-fake shape repeats for every external integration in the app; see the next section.
  • Kamal deploys the app as a Docker container to any server you control, with no PaaS subscription in the loop. See Deploying with Kamal.

The adapter+fake pattern

Every external service the app talks to (billing, email, bot checking, the App Store/Play Store receipt verification) follows one shape: a class exposes a .for(account)-style entry point that returns a real client when the relevant credentials are present, and a deterministic Local fake otherwise. app/adapters/billing.rb is the reference implementation. This is the single mechanism that lets a fresh clone of the kit run its entire feature set, end to end, with zero external accounts: the fakes aren't stubs that skip the flow, they run the real flow, in-process, against fake data.

Never call a third-party API directly from a controller or model. If you're adding a new integration, copy this shape; see The Adapter+Fake Pattern for how to build one.

Where things live

  • app/models/ is the domain, app/policies/ is authorization, app/adapters/ is every external integration.
  • config/routes.rb is the source of truth for every endpoint in the app; db/schema.rb is the source of truth for every table and column.
  • content/posts/ and content/docs/ are file-based markdown content (this site's blog and the docs you're reading now): no database table, no admin UI, published by committing a file.

Next

Read how tenancy and authorization sit on top of this stack: Accounts and Tenancy.