Edge overview — Versola Docs
VersolaVersola/docs
Pre-1.00.7.1versola.kzGitHub

Edge overview

What edge does in front of your API, which credentials it accepts, how a request moves through it, which ports it serves, and what it does not do.

edge is the traffic plane of Versola: a reverse proxy that signs users in, holds their sessions, checks every request to your API against the permissions and rules stored in central, and then forwards it. It also gives native mobile apps a back channel to auth. Read this page first if you want to put Versola in front of an API; you need a running installation with at least one tenant (Run Versola locally).

When to use edge

Use edge when you want the access decision made once, in front of the API, and not repeated in every service:

  • A browser app with no tokens in JavaScript. edge is the OAuth client. The browser only holds an EDGE_SESSION cookie (HttpOnly, Secure, SameSite=Strict).
  • Policy outside your code. Permissions, per-endpoint CEL rules, step-up authentication and identity injection are configuration in the admin console, not code in the service.
  • A native app under the fapi2 security profile. The app never holds a client credential. See Native apps.
  • Service-to-service calls. A client_credentials token is checked against the client’s permissions with the same pipeline.

Skip edge when your services already validate auth tokens themselves and you do not need a gateway. auth works without it.

Three ways to authenticate a call

edge reads the credential in this order and uses the first one it finds:

OrderCredentialTypical caller
1Authorization: Bearer <access token>Service or script holding an unbound token
2Authorization: DPoP <access token> plus a DPoP proof headerNative app, or any client whose token is bound to a key
3EDGE_SESSION cookie, set by /complete after a browser loginBrowser app

The token’s cnf claim, not the scheme the caller chose, decides what must be proven. The rules are listed in Sender-constrained tokens:

  • A token bound to a key (cnf.jkt) is refused over Bearer, with or without a dpop block in the config.
  • A token bound to a certificate (cnf.x5t#S256) is honoured only from the cookie, because the certificate is edge’s own.
  • A token bound to both a key and a certificate is refused on every presentation, including the cookie.
  • A DPoP header on a token that carries no key binding is refused.

When the cookie holds an expired access token, edge refreshes it with the stored refresh token and sends a rotated cookie. A header token is never refreshed.

Request lifecycle

Browser login through a preset

An authorization preset is a named login template on a client: redirect URI, post-login and post-logout URIs, scopes, response type, cookie domain and path. You create presets in the console, under the client.

  1. Your app navigates the browser to GET /login/{presetId}. edge stores a PKCE verifier and state for this login (valid 10 minutes) and answers 303 to auth /authorize. The query parameters acr_values, max_age, prompt, login_hint and ui_locales pass through. Everything else is ignored.
  2. The user signs in at auth, which redirects to GET /complete on edge with code and state.
  3. edge redeems the code, stores the encrypted refresh token in its own schema, sets EDGE_SESSION and answers 303 to the preset’s post-login URI. On an error from auth, it redirects to the same URI with error, error_description and error_uri appended.
  4. GET /logout/{presetId} sends the browser to auth /logout. auth then calls /logout/frontchannel and /logout/backchannel so edge drops the session.

The cookie value is <presetId>:<accessToken>. Its lifetime is the refresh token’s lifetime, or the access token’s when no refresh token was issued.

Calling the API

Your app calls /resources/{resourceId}/{path} with GET, POST, PUT, PATCH or DELETE. Other methods are not routed. edge then runs a fixed pipeline: token, revocation, binding, resource and endpoint lookup, permission, audience, optional userinfo, the allow rule, step-up, injection, forward. Any step can end the request. The steps, the CEL context and the failure codes are on Endpoint policies.

Forwarding rules:

PartWhat the resource receives
AuthorizationInternal resource: Basic with the resource ID as user and the base64url-encoded resource secret as password. The caller’s token is not forwarded. Public resource: the caller’s token as Bearer
CookieEvery cookie except EDGE_SESSION
Host, DPoP, Content-LengthRemoved
Inject rulesHeader, query and top-level JSON body values written by CEL. They replace what the client sent
ResponseStatus and body as the resource sent them, minus Set-Cookie. edge reads the whole response before sending it

A caller that wants to hide UI controls can ask GET /permissions/me?resource=<id> (repeat the parameter for several resources). It returns the endpoint IDs the caller’s roles allow. The proxy enforces the same rules independently.

What edge does not do

  • It does not terminate TLS. It serves plain HTTP on PORT; put a TLS-terminating proxy in front. The Secure cookie needs HTTPS.
  • It does not rate-limit, cache, rewrite paths or filter content.
  • It does not stream responses. A large upstream response is buffered in memory first.
  • CEL rules see the token, the userinfo claims, the path parameters, the query, the headers and a JSON body. They cannot call out to another service.
  • Body injection works on a top-level JSON object only, and every injected value is a string.
  • It does not issue tokens or hold user data. auth does.

Ports and operational endpoints

PortVariableDefaultServes
ApplicationPORT8080Everything on this page
DiagnosticsDPORT8081GET /liveness, GET /readiness, GET /metrics

edge does not use APORT or MPORT. On a CLI-generated vps deployment edge listens on 8095 and 8096. POST /service/configuration/sync makes edge reload its configuration from central at once. It exists for tests, needs Basic credentials built from security.internal-secret, and answers 404 when env is prod. See Edge configuration and Observability.

Limitations

  • One Postgres for all replicas. Every replica of edge uses the same Postgres: edge’s own database, or the edge schema in a shared one in the simplest setup (the CLI local and vps targets). Sessions, pending logins, revocations and DPoP replay records live there, and replicas coordinate through it.
  • Revocation spreads through Postgres LISTEN/NOTIFY. A revoked token is rejected from an in-memory list that every replica keeps current. LISTEN does not work through a transaction-mode pooler; set postgres.notifications-url to a direct URL (Behind a connection pooler). A reconcile every revocation.reload-interval (default 600 s) is the backstop for a missed notification. A replica that cannot load the list at startup refuses to start.
  • Configuration lags. Resources, endpoints, permissions, clients and presets are cached and reloaded every configuration-cache-refresh-interval. Removing a permission reaches each replica within one interval. Revoking a token does not wait for it.
  • Native apps need the native block, and native needs dpop. Without them every /native/* call answers 404.

Next steps