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](/docs/deployment/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](/docs/mobile-apps/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](/docs/deployment/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](/docs/building-features/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](/docs/core-concepts/accounts-and-tenancy).
