Protected environments
Require sign-in for every public endpoint in an environment, enforced at the edge, with a signed header that tells your service who is signed in.
A protected environment requires sign-in before any request reaches your services. Specific enforces this at the edge, so it applies to every public endpoint and every domain in the environment without any application code.
Turn it on per environment from the dashboard, under the environment’s settings: Require sign-in for services. Toggling it redeploys the environment so it takes effect immediately.
Protected environments are included in the Scale plan and above; see the pricing page.
Who can sign in
Members of the Specific organization that owns the project. Signing in goes through the same identity provider you use for Specific itself, and anyone who isn’t a member of the owning organization is refused.
That makes protected environments a fit for internal tools, staging environments, and admin panels. It is not a login for your own end users, who don’t have Specific accounts. Build that authentication in your application.
Identifying the signed-in user
Once an environment is protected, every request reaches your service with an X-Specific-Token header: an HS256-signed JWT describing who is signed in. Reference the endpoint’s token_secret in your specific.hcl to receive the verification key:
service "web" {
build = build.web
endpoint {
public = true
}
env = {
SPECIFIC_TOKEN_SECRET = endpoint.default.token_secret
}
}
Only services that ask for the key receive it, and it is scoped to a single endpoint of a single environment: a token minted for staging can’t verify in production. See Endpoints for the other endpoint reference attributes.
The token’s claims:
| Claim | Meaning |
|---|---|
sub |
Stable Specific user ID (user_…). Use this to key your own records. |
email |
The signed-in user’s verified email. Absent if we couldn’t verify one. |
host |
The hostname the token was issued for. |
env |
The environment ID (env_…). |
iat |
Issue time. |
exp |
Expiry, 24 hours after sign-in. |
A few things that differ from a typical auth setup:
- The header is not stripped on unprotected endpoints, so a request carrying one doesn’t mean protection is on. Only a valid signature makes it trustworthy; treat a missing or unverifiable token as unauthenticated.
- The
__specific_sess_v2cookie is the session credential, not an identity assertion. Don’t parse it; the header is the supported interface. - Any service in the same environment can send this header to another. The trust boundary is the environment, not the individual service.
Revocation
The signed-in session lasts 24 hours. Removing someone from the organization stops them signing in again, but an existing session keeps working until it expires.
Local development
specific dev runs your services directly on localhost with no edge in front, so no X-Specific-Token header is sent locally. endpoint.<name>.token_secret still resolves, to a fixed development value, so your service starts normally.
Treat the missing header as unauthenticated and add a dev-only fallback user that can’t be reached in production. Because the local key is a known constant, you can also sign your own tokens with it to exercise your authorization logic against specific dev.