Real accounts, without the password problem.
Sign-up and sign-in for the people who use your app. A code in their inbox, a passkey on their phone, a session, a sign-out button, and a login page. Two lines of config, on every plan, including free.
You ask for accounts. It ships a login.
No identity dashboard, no client secrets, no callback URLs. Your agent adds a block to the project config and declares who each route is for. Deploying that mounts the sign-in endpoints, creates the account tables, and puts the browser client on every page.
# end users: people who sign up on your app
[auth]
enabled = true
providers = ["email"] # codes; passkeys ride along
# each person sees only their own rows
[db.tables.books]
read = "own"
write = "own"
import { db } from 'hatchable';
export const access = 'user';
export const methods = ['POST'];
export default async function (req, res) {
// req.user is guaranteed here. Anyone signed
// out was turned away before this ran.
await db.query(
'INSERT INTO books (user_id, title) VALUES ($1, $2)',
[req.user.id, req.body.title]
);
res.json({ ok: true });
}
Everything sign-in needs, and nothing to run.
The accounts, the sessions, the ceremonies, and the abuse limits are the platform's job. What is left for your app is the part that is actually yours: what a signed-in person can see and do.
A code in the inbox, never a password
Someone types their address and gets a six-digit code. It is good for ten minutes, five wrong guesses burn it, and it is stored hashed rather than in plain text. First sign-in creates the account; there is no separate sign-up form.
Nobody picks a weak password, because nobody picks a password.
Face ID and Touch ID, included
After the first sign-in your app can offer a passkey, and returning visitors are one prompt away from being back in. Saved passkeys show up in the email field's autofill, and if a person cancels or their device cannot do it, the code path is still there.
A regular comes back on Tuesday and never opens their inbox.
Continue with Google, under your name
Add google to the providers list and the button appears once you finish a one-time console setup: a domain you have verified, plus your own Google client, so the consent screen shows your brand rather than ours. Email stays available alongside it.
Sign up by email in March, use Google in June, still one account.
Your customers and your team are different people
Every route says who it is for in one line. user means someone who signed up on your app. member means a Hatchable collaborator on the project, which is you and whoever you invited to build it. admin narrows that to owners, and scheduler is for background jobs only.
The customer dashboard is user. The staff console is admin. They never overlap.
Stay signed in, and sign out for real
Signing in sets a cookie that JavaScript cannot read, scoped to your app and kept separate from the Hatchable session you build with. It lasts thirty days, refreshes while a person keeps using the app, and expires for good after six months. Sign-out is one call.
No session store to run, no token rotation to think about.
A sign-in screen that looks like your app
There is always a working page at /login, drawn with your theme file and your logo, so sign-in can never dead-end. Put your own page at that path and yours wins automatically. Signed-out visitors are sent there and returned to whatever they were trying to reach.
Ask for a login screen in your own colors and you get one.
Anonymous is a design decision, not an error
A public route can serve both audiences from one handler: read the signed-in person, get them or get nothing, and vary the page. Signed-out is the normal case for a visitor who just arrived, so it should look like a front door, not a rejection.
Anyone can read the recipes. Saving one is what asks you to sign in.
Close the door when the audience is a list
Switch sign-up to invite and the rule becomes: an existing account, or an address you invited. Invites are rows your admin pages add and remove, never a list sitting in a config file, and an uninvited address is refused before any code is sent.
A client portal where the only people who can get in are the ones you added.
The spec sheet, in plain terms.
[auth] block in hatchable.toml. Available on every plan, including free.public, user, member, admin, scheduler. Declared per file, enforced at the edge before your code runs, and required on every route.req.user is there: id, email, name, and profile image. On a public route you read it yourself and it may be empty.Numbers current as of August 2026 and enforced by the same table the pricing page renders from.
Say it like this.
You never have to name a library or a provider. These are ordinary sentences that turn into accounts, gates, and a login screen.
Asked and answered.
Do I need a separate auth service?
No. Sign-in is part of the platform, the way the database is. There is no identity provider to sign up for, no keys to paste, no callback URLs to configure, and no per-user bill on top of your plan. You turn it on in your project config and deploy.
Are there passwords anywhere?
No. There is no password field, no reset flow, and no password column in the database. People prove they own an email address with a one-time code, then add a passkey if they want a faster way back in. A whole category of support request stops existing.
What is the difference between a member and a user?
They are two different populations. A member is a Hatchable collaborator on your project, meaning you and anyone you invited to help build it. A user is someone who signed up on your deployed app. A route declared for members lets your team in and turns your customers away; a route declared for users does the opposite. Being a collaborator does not make you a user of your own app, so if you want to use it, sign up like everyone else.
Do I have to build the login page myself?
Only if you want it to look like your app, which you usually do. The platform serves a working sign-in page at /login, styled from your theme file and your logo, so sign-in can never dead-end. Ship your own page at that path and yours takes over automatically, with no flag to flip.
Can people sign in with Google?
Yes, as an addition to email, once you connect it. You add google to the providers list, then finish a one-time setup in the console: a verified custom domain, which is Google's requirement, and your own Google OAuth client, so the consent screen shows your name and logo rather than ours. Someone who signed up by email and later uses Google with the same address stays one account.
How many people can sign up, and can I limit who does?
The free plan includes 100 end-user accounts, Builder 1,000, and the App plan 5,000 per project. If your app is meant for a controlled audience instead, set signup to invite: only existing users and emails you have invited can create an account, and uninvited addresses are refused before any code is sent.
More detail lives in the [auth] reference and the SDK reference.
Stop wiring up a login flow.
Connect the AI you already use, say that people should be able to sign up, and the accounts are simply there: codes, passkeys, sessions, and a page to sign in on.
Want the technical surface? The [auth] reference covers providers, invite mode, and the login path.