Access control
Decide who can reach a deployment. This is separate from login for your app's users.
Use it to protect a pre-launch deployment or to show it only to specific people. There are four modes.
runlot access set public # Anyone can reach it (default)
runlot access set org # Only members of this organization can reach it
runlot access set password # Only people who know the shared password can reach it
runlot access set app # Only people signed in to this app can reach itrunlot access # Show the current setting
runlot access --jsonRunning these commands requires the admin role.
password mode
runlot access set password --password 'my-secret'If you omit --password, the terminal prompts you for it. Visitors to a protected deployment see a password prompt before the app.
org mode
Members of the organization get in with their dashboard login. Anyone who is not a member of the organization receives a 403 response.
app mode
runlot access set app # Only people signed in to this app can reach itOnly requests that carry a valid login session for this project reach your code. Everything else is answered by the platform:
| Request | Answer |
|---|---|
A browser GET or HEAD asking for HTML | 302 to /__runlot/auth/sign-in?next=<path> |
| Any other request | 401 with {"error":{"code":"sign_in_required"}} |
/__runlot/auth/* | Always passes, so people can sign in |
| A request with the bypass secret | Passes, the same as in org and password |
- Static assets are not exempt. A protected app's JavaScript bundle is protected content too.
- Path rules still apply after admission, and still only narrow.
appneeds login turned on for the project; otherwise the command answers409 login_required. While the mode isapp, turning login off is refused with409 access_mode_app; choose another mode first.- A request with no session cookie is refused at the front without starting the project. A request that carries a cookie starts the project so the session can be checked against its database.
Reading access information in your worker
The access information verified at the front is passed to your worker as request headers.
| Header | Value |
|---|---|
Runlot-Access-Mode | public · org · password · app · bypass |
Runlot-Access-Subject | User ID (varies by mode) |
Runlot-Access-Email | Login email (org mode) |
Runlot-Access-Org-Role | admin · member · viewer (org mode) |
You can read them with the helpers we provide.
import { identity, isSignedIn } from "@runlot/access";
const who = identity(request);
// { mode: "org", subject: "…", email: "[email protected]", orgRole: "admin" }
if (who.orgRole !== "admin") return new Response(null, { status: 403 });Any runlot-* header sent by a client is stripped at the front, and only verified values are added back. A client therefore cannot forge these values.
In a local environment that runs without the front, mode is public and every other value is null.
Bypassing from CI
Use this when automation needs to reach a protected deployment.
runlot access bypass --newThe secret is shown only once. There is no way to see it again afterwards.
curl -H "Runlot-Access-Bypass: <secret>" https://my-app.me.runlot.app/healthrunlot access bypass --revokeUnauthorized requests never start the project
Access is checked before the project starts. Even when an unauthorized request arrives, an automatically stopped project is not started, and that request is not counted toward usage.
Path rules
runlot.json can narrow the mode for individual paths. Rules travel with the deployment, so rolling back restores the old list.
{
"access": [
{ "path": "/admin/*", "require": { "orgRole": "admin" } },
{ "path": "/internal/*", "require": { "emailDomain": ["acme.com", "acme.co.kr"] } }
]
}pathis an exact path, or a prefix ending in/*that also covers the prefix itself (/admin/*covers/admin).*is not accepted anywhere else.require.orgRoleis the minimum organization role:viewer,memberoradmin.require.emailDomainis one domain or a list. Every condition you set must hold, and every rule whose path matches must hold.{}admits anyone the mode already admits.- A request a rule refuses gets
403. At most 64 rules. - Rules only narrow. They can never open a path the mode keeps closed.
- Rules are ignored while the mode is
public.runlot deploywarns when that is the case. Switch toorg,passwordorappto enforce them. - Under
appmode, a rule may not requireorgRoleoremailDomain. People signed in to your app carry no organization role or Runlot email, so such a rule could never be met. A deploy that adds one to anappproject, and switching toappwhile the live deployment has one, are both refused with409 access_mode_app_rules. Rules with an emptyrequireare fine. - Visitors who entered with the shared password, or requests with the bypass secret, satisfy only rules with an empty
require, because they carry no organization role or email.
Current limitations
password mode limits repeated wrong guesses: after several failures in a row, further attempts are answered with 429 Too Many Requests for a while. That slows guessing down; it does not make a short or easily guessed password safe.