Self-hosted · Configuration

Sign-in and identity

Local accounts, GitHub, Google and your own OpenID Connect provider — and the one detail that breaks more first setups than everything else combined: the redirect URI. Sign in with the email on your subscription.

We send a 6-digit code. Access is limited to emails on an active self-hosted subscription — talk to sales if you do not have one yet.

We sent a code to . It expires in 10 minutes.

Self-hosted · Configuration

Sign-in and identity

Four ways your users can sign in. Read section 03 before you register anything with an identity provider — the redirect URI is the single most common reason a correctly-configured provider still fails.

01

What you can turn on

Local email and password always works and needs no configuration. Everything else is additive: configure a provider and its button appears on the sign-in card; leave it unconfigured and there is no button.

MethodSettings it needsWhere you set themReaches the internet?
Local
email + password
None. Works on a fully disconnected network. Nothing to configure No
OpenID Connect
Entra ID, Okta, Keycloak, Auth0, Ping…
TV_OIDC_ISSUER, TV_OIDC_CLIENT_ID, TV_OIDC_CLIENT_SECRET — all three required. Optional TV_OIDC_LABEL, TV_OIDC_SCOPES. Console → Single sign-on Only to your IdP
Google TESTVIBE_GOOGLE_CLIENT_ID, TESTVIBE_GOOGLE_CLIENT_SECRET Console → Single sign-on Yes — accounts.google.com
GitHub TESTVIBE_GITHUB_CLIENT_ID, TESTVIBE_GITHUB_CLIENT_SECRET Console → Single sign-on Yes — github.com
Air-gapped sites Google and GitHub sign-in reach accounts.google.com and github.com from the user's browser and from the TestVibe server. Neither works without outbound internet. Use local accounts, or OpenID Connect against an IdP inside your own network.
SAML is not supported, and is not planned. Every mainstream enterprise IdP — Entra ID, Okta, Keycloak, Auth0, Ping, ADFS 2019+ — speaks OpenID Connect. Use the OIDC path.
Console or environment — and what "locked" means Every key above has the same name in the console and in the environment. The environment is the floor: a key pinned in .env, Helm values or testvibe.env is shown read-only in the console and can never be silently overridden from a web page. On the appliance nothing is pinned by default, so use the console. Secrets you type there are encrypted and never sent back — the field shows only "set" or "not set". A saved change reaches the running application within about a minute, and no restart is needed for a new button to appear.
02

Local accounts

The path that always works, including on a fully disconnected network. There is nothing to enable.

You do not need email to get people in

Signing up on the sign-in card does send a verification link, and sign-in is refused until it is clicked. Neither of the two ways people actually arrive goes through that door:

  • The first user is created in the admin console under First user, already verified, able to sign in immediately. It needs TV_BOOTSTRAP_TOKEN on the web service and the same value as TV_ADMIN_BOOTSTRAP_TOKEN on the console, and it works only while the install has no users at all. The ordered procedure is Creating the first application user.
  • Everyone after that is invited from Settings → Members → Invite inside the application, which produces a copyable link you can send however you like. Signing up through that link with exactly the invited address skips verification.

A mail relay is still worth having — password resets, notifications and emailed invitations need it — but it is not a gate on getting started. Set it up when you are ready: Integrations → Mail.

Single sign-on is not a shortcut around verification A brand-new account arriving through OpenID Connect, Google or GitHub is still asked to confirm its email address, even when the provider already told TestVibe the address is verified. Use the First user panel, or an invitation link.

Who may sign up

TV_SIGNUP_MODE (Console → First user → "Who may sign up") decides whether a stranger who can reach your sign-in page can create a login.

ValueLabelMeans
inviteBy invitation onlyOnly someone holding an invitation link can sign up. This is the default.
openAnyone who can reach this addressAnybody who can open the sign-in page can create their own login.
closedNobody — you create every userSelf-registration is off. Every login is one you invite.

Passwords and lockout

Both are enforced identically on sign-up, password reset and change-password, and neither is configurable.

  • 8 to 128 characters, with at least one letter and one number. The form states this as "At least 8 characters, including a letter and a number." A short blocklist of breached-but-compliant passwords (passw0rd, welcome1) is rejected, as is a password equal to the user's email address or its local part.
  • 5 failed attempts in a 15-minute sliding window locks the email address. The cool-down doubles across a streak — 1 → 2 → 4 → 8 → 15 minutes, capped.
  • While locked, even the correct password is refused. The banner reads "Too many sign-in attempts. Try again in N minutes, or reset your password." and offers a reset link.
  • An unknown email counts the same as a wrong password, deliberately — so the lockout cannot be used to discover which addresses have accounts.
  • The counter is in memory, per process. Restarting the web service clears it. That is a first line of defence, not a durable one.
If you point TestVibe at TestVibe Generating tests against an application with a login form means the AI agent drives that form repeatedly, which can trip its own lockout. An operator allowlist — TV_LOGIN_THROTTLE_EXEMPT_USERS (comma-separated emails, empty by default) — raises the failed-attempt ceiling for those identities to TV_LOGIN_THROTTLE_EXEMPT_MAX_ATTEMPTS (default 100) before the same escalating lockout engages. It is a raised ceiling, never a bypass: a wrong password still fails. Use it only for a low-value test account you own.

Two cloud behaviours do not apply here. The per-IP sign-up throttles fail open on a self-hosted install. And new-signup notifications go nowhere unless you set TESTVIBE_SIGNUP_NOTIFY_EMAIL (Console → Mail → "Notify on new sign-ups") — this install never reports its users outside your network.

03

The redirect URI

Read this once, carefully. Every OAuth and OIDC provider refuses to send a user back to an address you did not pre-register. The error is raised on their page, so nothing appears in your logs and there is no clue in TestVibe pointing here.

The rule, in one sentence TestVibe asks the provider to return the user to the exact URL the user's browser was already on — scheme, host, port and path, with any query string and any #fragment removed. Register that.
1 · The browser is at https://testvibe.example.com/ ?tab=x#/features/42 2 · TestVibe sends as redirect_uri https://testvibe.example.com/ 3 · Your identity provider compares it against the list you registered MATCH → the user comes back signed in https://testvibe.example.com/ …is in the registered list NO MATCH → the provider's OWN error page redirect_uri_mismatch / AADSTS50011 TestVibe never hears about it. No log line.
Diagram — where the redirect URI comes from and who rejects it.

Work out your value in three steps

  1. Open TestVibe the way your users will and look at the address bar. Not the way you reach it from the hypervisor console; the way a person at a desk reaches it.
  2. Delete everything from the first ? or # onwards. TestVibe's deep links live in the fragment and are stripped; the fragment is restored after sign-in succeeds, so you never register one.
  3. Make sure it ends in a slash. A bare origin normalises to a path of /, so the value TestVibe sends always has a trailing slash. https://testvibe.example.com and https://testvibe.example.com/ are the same address to a browser but not the same string to Google. You should end up with one line you can paste straight into your provider.
How your users reach TestVibeRegister exactly
Appliance, DNS name, through the port-80 front door http://testvibe.corp.example.com/
Appliance, by IP, through the front door http://10.20.30.40/
Appliance, application port directly http://10.20.30.40:8080/
Docker Compose, published web port http://testvibe.corp.example.com:3000/
Behind your own TLS reverse proxy (the normal production shape) https://testvibe.example.com/
Register every address people actually use If some users bookmark the IP and others use the DNS name, or some arrive on :8080 and others through port 80, those are different redirect URIs and each must be registered. Google and most OIDC providers match the string exactly. The cleaner fix is to pick one address, publish it, and redirect the others to it.

Keep the public base URL in step

TESTVIBE_PUBLIC_BASE_URL (Console → Addresses → "Application URL") is not what the sign-in flow sends as the redirect URI — the browser's own address is. But it is what emailed links and the runner's artifact upload are built from. Set it to the same address you registered. Never localhost, never a container or service name; it must be reachable from a user's browser.

The console's "Redirect URI" card is a guess — verify it The Single sign-on panel prints a Redirect URI copy line, derived from TESTVIBE_PUBLIC_BASE_URL in the console's own process environment. On the appliance that variable is deliberately not pinned there (the address is seeded into the database instead so it stays editable), so the card falls back to the console's internal health-probe address and shows http://127.0.0.1:8080/. Do not register that — it is a loopback address and no browser will ever come back to it. Use the address-bar rule above. On Compose and Kubernetes, where TESTVIBE_PUBLIC_BASE_URL is pinned in the environment, the card is correct.
04

Connecting a provider

One procedure covers all three. TestVibe is an ordinary confidential web application doing the authorization-code flow, so every provider wants the same four things: a web app registration, a redirect URI, scopes that release the email address, and a client ID plus secret.

Register the application

  1. Register a Web application / confidential client with your provider — not a SPA, not a native app. TestVibe holds a client secret and exchanges the code server-side. The table below gives the exact menu path.
  2. Set the redirect URI to the value from section 03, and add every address your users use.
  3. Grant the scopes and check the application actually releases the email claim. TestVibe keys identities on a verified email address; a token with no email is the second most common failure after the redirect URI.
  4. Copy the client ID and secret into Console → Single sign-on, into the fields named below. For OIDC, also paste the issuer.
  5. Reload the application's sign-in page. You should see the new button. If it is missing, the provider group is incomplete — the console shows it as Incomplete and names the missing keys.

What differs per provider

OpenID ConnectGoogleGitHub
Where you register Your IdP's app registration screen Google Cloud console → APIs & Services → Credentials → OAuth client ID → Web application GitHub → Settings → Developer settings → OAuth Apps → New OAuth App
Field for the redirect URI Redirect URI Authorized redirect URIs Authorization callback URL
Scopes openid email profile openid email profile — identity only repo read:user user:email — see the warning below
Console fields OpenID Connect issuer URL, Client ID, Client secret, Button label, Scopes Google OAuth client ID, Google client secret GitHub OAuth client ID, GitHub client secret
How it matches the URI Exactly, on most providers Exactly — the trailing slash matters most here By prefix, so the bare base URL usually covers every path under it
Button reads "Continue with {Button label}" — default SSO Continue with Google Continue with GitHub

For OIDC, all three of issuer, client ID and client secret must be present before the button appears, and TV_OIDC_SCOPES defaults to openid email profile.

The OIDC issuer value

Give TestVibe the issuer, not the authorize URL. It fetches {issuer}/.well-known/openid-configuration and discovers the rest.

TV_OIDC_ISSUER
Microsoft Entra ID   https://login.microsoftonline.com/<tenant-id>/v2.0
Okta                 https://<org>.okta.com
Keycloak             https://<host>/realms/<realm>
Auth0                https://<tenant>.eu.auth0.com/
Ping                 https://<host>/<env-id>/as

Verify it before you save. Run this on the TestVibe host — you should get JSON back:

check the discovery document
curl -fsS https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration | head -c 300
Discovery is resolved before the browser is sent anywhere. If the issuer is wrong or unreachable you get an error on TestVibe's own sign-in card rather than a dead redirect — which is how you tell an issuer problem from a redirect-URI problem.

Provider-specific traps

Google will not accept a private address Google requires redirect URIs on a public, resolvable host. An install users reach at http://10.20.30.40:8080/ or a .internal name cannot use Google sign-in at all. Put it behind a public DNS name with TLS, or use OpenID Connect against your own IdP.
GitHub's scopes are broader than identity GitHub sign-in requests repo read:user user:email. The repo scope is there because the same OAuth client also backs TestVibe's optional repository export, so a user consenting to sign in is consenting to repository access as well. If that is unacceptable in your organisation, use OpenID Connect or local accounts.
GitHub Enterprise Server is not supported for sign-in The authorization, token and user endpoints are fixed to github.com and api.github.com; no setting points them at a GHES host. Registering an OAuth app on your own GitHub Enterprise Server will not work — the browser is sent to github.com regardless. (The console's own help text on this field suggests otherwise; it is wrong.) Use OpenID Connect, which most GHES estates already have alongside it.
05

One person, many logins

Turning on a second provider does not create second accounts for people who already have one.

  • Identities are keyed on a verified email address. One person is one user record; each sign-in method is an identity row attached to it. Signing in with Google as ana@yourcompany.com after having a local account on the same verified address lands on the same user.
  • An unverified provider email is never auto-linked. If the provider will not vouch for the address, TestVibe asks the user to confirm it and link explicitly in Settings.
  • Users belong to one or more workspaces. Every new user gets a personal workspace; team workspaces are created in the app and people are invited to them. Roles are owner, admin and member. None of that is operator configuration.
  • Changing an email address is verify-before-commit. The new address gets a confirmation link and the old one gets a security notice; the live address does not move until the link is used.
Rolling out SSO to an existing install is safe. Configure the provider, tell people to use the new button, and existing accounts pick up the new identity on first use — provided the provider releases the same verified email address they already have.
06

Troubleshooting sign-in

SymptomAlmost alwaysFix
The provider shows its own error page — redirect_uri_mismatch, AADSTS50011, "The redirect URI in the request does not match" — and the user never returns. The registered redirect URI is not the address the browser was on. Section 03. Check the trailing slash, the port, and http vs https.
The button does not appear on the sign-in card at all. The provider group is incomplete — for OIDC, all three of issuer, client ID and secret are required. Console → Single sign-on. The group shows Incomplete and names the missing keys.
Sign-in fails on TestVibe's own card, before any redirect. Discovery failed — wrong issuer, or the IdP is not reachable from the TestVibe host. Fetch {issuer}/.well-known/openid-configuration from the host and fix whichever it is.
The user comes back but is asked to type their email address. The provider released no email claim, or an unverified one. Grant the email scope and check the claim mapping on the IdP side.
Somebody signed up on the card and now cannot sign in. No mail relay, so the verification link went to the log. Send them an invitation link from Settings → Members → Invite instead — it needs no email. Or configure Mail and use the resend-verification offer on the sign-in card.
Sign-in worked yesterday, everything broke today, nothing changed. The install's address moved — a DHCP lease, a new proxy hostname — so the browser is now on an address that was never registered. Give the install a stable address, re-register the redirect URI, and update Console → Addresses to match.

Where to go next