Docs · Core Concepts

Roles and Teams

Member and admin roles today, and the documented path to multi-user teams later.

One Shot ships two roles today, member and admin, and one user per account. That's a narrower model than most SaaS products eventually want, and it's narrow on purpose: it's the smallest thing that's correct, with a documented path to grow past it without a rewrite.

What exists today

User#role is either member or admin. There's no admin UI: an admin operates on data through bin/rails console, working through an account the same way any other code does (account = Account.find(...)). The main thing admin? does today is exempt an account from entitlement checks, so an admin can reach a paid controller without a subscription; see Authorization. You can extend the same flag to gate an admin area of your own if you build one.

Because there's exactly one user per account, "who's on this team" isn't a question the app has to answer yet. The account is the user, for authorization purposes, and every policy, every scoped query, and every billing check already keys off Current.account rather than Current.user. That's the distinction that makes the next section additive rather than disruptive.

Adding real multi-user teams

When you need more than one person sharing a workspace, the model doesn't need to move much, because everything already scopes to the account rather than the user:

  1. Add a join model with a role, something like Membership, joining account, user, and a role column (owner, member, whatever your product needs). Let User belong to many Accounts through it.
  2. Add owner_id to Account, and set it to today's implicit single user so existing accounts keep working with no migration of behavior, only of data.
  3. Add an account switcher in the signed-in UI that sets Current.account to whichever workspace the user picked. This is the one new piece of session state; everything downstream already reads Current.account.
  4. Build the invite flow: an invite record, an email, and an acceptance path that creates the Membership. This is genuinely new work, unlike the first three steps.

What you will not need to do: touch every model to add a new scoping column, rewrite policies that already filter through ApplicationPolicy::Scope, or change how billing attaches (it's already on Account, not User, specifically so a shared subscription needs no rework). The tenancy boundary was the expensive decision, and it's already made; see Accounts and Tenancy for why it was made this way from the start.

Next

With the domain model clear, see how to add a feature on top of it: Adding a Resource.