Skip to content
runlot

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 it
runlot access            # Show the current setting
runlot access --json

Running 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 it

Only requests that carry a valid login session for this project reach your code. Everything else is answered by the platform:

RequestAnswer
A browser GET or HEAD asking for HTML302 to /__runlot/auth/sign-in?next=<path>
Any other request401 with {"error":{"code":"sign_in_required"}}
/__runlot/auth/*Always passes, so people can sign in
A request with the bypass secretPasses, 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.
  • app needs login turned on for the project; otherwise the command answers 409 login_required. While the mode is app, turning login off is refused with 409 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.

HeaderValue
Runlot-Access-Modepublic · org · password · app · bypass
Runlot-Access-SubjectUser ID (varies by mode)
Runlot-Access-EmailLogin email (org mode)
Runlot-Access-Org-Roleadmin · 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 --new

The 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/health
runlot access bypass --revoke

Unauthorized 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"] } }
  ]
}
  • path is an exact path, or a prefix ending in /* that also covers the prefix itself (/admin/* covers /admin). * is not accepted anywhere else.
  • require.orgRole is the minimum organization role: viewer, member or admin. require.emailDomain is 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 deploy warns when that is the case. Switch to org, password or app to enforce them.
  • Under app mode, a rule may not require orgRole or emailDomain. 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 an app project, and switching to app while the live deployment has one, are both refused with 409 access_mode_app_rules. Rules with an empty require are 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.

On this page