Edge The site gate
A deployed site is public the moment it lands. The site gate puts HTTP Basic auth in front of it: a visitor gets the browser's own password prompt, and without a username and password the gate accepts, never reaches the site. It is the wall for a site that is not ready to be found, kept off search engines and out of a forwarded link, rather than an authorization model: anyone holding a password reaches everything the gate fronts.
What it covers
Canvas paths, and only Canvas paths. Every site the slice hosts, at every route, sits behind the one gate; there is no way to gate one site and leave another open. A path under /api/ or /trigger/ is never asked for this credential, because both carry their own in the same Authorization header, and a gate there would collide with an app's Bearer tokens and API keys. Guard a Pulse with an auth block instead.
The gate runs after the address allowlist and after the rate limit, so a visitor guessing passwords spends the slice's per-minute budget like any other request, and a client the allowlist refuses never reaches the prompt.
Declaring it
edge {
site_auth {
realm = "Staging" # what the browser's password box says
user "alice" {
password = env("ALICE_PASS") # the NAME of an environment variable
}
user "bob" {
password = env("BOB_PASS")
}
}
}A password is never written in the file. The Driftfile is committed, so each password is env("NAME"), naming a variable that drift apply reads when it runs: from your shell, a --var NAME=value flag (--secret does the same), or the .env beside the Driftfile. A literal is refused, and the refusal never prints the value:
Driftfile:15:18: A secret is a reference: site_auth user "alice": password is write-only and must be written env("NAME"): a literal here would be a credential committed with this fileA variable that is unset or empty refuses the whole file before anything is sent, naming the user and the variable and never a value: edge.site_auth: the password for user "alice" is read from $ALICE_PASS, which is not set. Export it, pass --secret ALICE_PASS=..., or add it to the .env beside the Driftfile. The ... there is the CLI's own placeholder for the value.
Present, the block is the whole credential set: the users it names are exactly who can sign in. The apply says who that is, and who it shut out:
✓ site gate set: alice, bob can sign in
removed: carolAbsent, the gate stays as it is, on or off. No declaration turns the gate off, because that is the one direction that exposes the site: delete the block and run drift slice auth disable, which asks first. drift plan shows the gate and each user: + site_auth when the gate would be turned on, + site_auth alice password from $ALICE_PASS for a user who would be added, site_auth alice present; password set again from $ALICE_PASS on apply (write-only, not compared) for one who stays, - site_auth carol removed for one who would be shut out (which drift apply asks you to agree to), and ! site_auth alice $ALICE_PASS is not set here: apply would refuse for a variable that is missing. The block is described in the Driftfile reference.
From the CLI
The commands act on the active slice, for a one-off change or a slice no Driftfile manages.
| Command | What it does |
|---|---|
drift slice auth set --user <name> [--realm R] | Turn the gate on for the users named, replacing every credential already set. Repeat --user for more than one. |
drift slice auth set --user <name> --password-stdin | The same, reading one password per --user, in order, from stdin, for CI |
drift slice auth list | Whether the gate is on, its realm, and who can sign in |
drift slice auth disable | Remove the gate, after a confirmation |
set prompts for each password with echo off. There is no --password flag, because the value would land in ps output and shell history.
$ drift slice auth set --user alice --user bob --realm Staging
Site gate enabled. alice, bob can sign in.
Realm: Staging
Visitors without the password now get a 401 instead of the site.$ drift slice auth disable
This makes the site publicly reachable by anyone with the URL.
Remove the site gate? [y/N]Answering y prints Site gate removed. The site is public again.; anything else prints Left as it was. and changes nothing. list prints No site gate. The site is public. for a slice without one.
What a visitor sees
Without a credential, or with a wrong one:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
WWW-Authenticate: Basic realm="Staging"
{"error":"authentication required"}The WWW-Authenticate header is what makes a browser draw its password box. A gate with no realm advertises Restricted. When the slice's Deed cannot be asked (it is restarting, or cannot read its own record), the answer is:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"error":"site authentication is unavailable"}A gate whose checker is down stays shut. No failure on the way to Deed is ever turned into an admission.
Who checks the password
Edge decides, Deed verifies. The router decides that a Canvas request must carry a credential; whether the credential is good is the slice's own Deed to say.
sethashes before anything leaves the front door. The api bcrypts each password as it arrives, so the plaintext never travels further into the platform.- The digests live sealed in the slice. The record is pushed into the slice's Deed, which keeps it encrypted under the slice's own key, beside Vault, Link and Pocket. Once Deed holds it, Drift's control plane keeps only the gate's shape: whether it is on, the realm and the user names. It holds no digest.
- The router asks; it never looks. For a visitor's credential, the router sends the name and password, in a request body, to the one route Deed serves on its gate port,
POST /verify, and acts on the yes or no that comes back. Nothing else of Deed's is reachable on that port, so the router can ask the question without being able to read a digest, a Vault entry or a Pocket item. - A pass is remembered briefly; a failure never. A credential that cleared the gate is not asked about again for up to 60 seconds, so a browser resending it on every request costs Deed about one check a minute. A refusal is never remembered. One consequence: a user you remove can keep reaching the site for up to a minute.
A slice whose Deed cannot take the record yet (an older slice runtime, or one that was unreachable when you set the gate) keeps it in the control plane, and the router checks the digests there until a background pass, every five minutes, moves it into Deed. Either way the gate you set is the gate that is enforced.
Limits
| Limit | Value |
|---|---|
| Users | 1 to 32. A gate nobody can pass is refused. |
| Username | 1 to 64 characters, no colon (Basic auth splits on it) and no control characters; a Driftfile also refuses whitespace. Each name once. |
| Password | At least 12 characters and at most 72 bytes, the most bcrypt can hash. Unlike an account password, it is not screened against lists of breached passwords. |
| Realm | At most 64 characters, with no ", no \ and no control characters |
| Scope | Slice-wide, Canvas paths only |