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/devneed 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_laterjust works, with no Redis to provision. - Propshaft serves assets, and Importmap serves JavaScript straight from
app/javascript/controllers/with no bundler, nonpm 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.rbis the source of truth for every endpoint in the app;db/schema.rbis the source of truth for every table and column.content/posts/andcontent/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.