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.
| Method | Settings it needs | Where you set them | Reaches 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 |
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 |
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.
.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.
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_TOKENon the web service and the same value asTV_ADMIN_BOOTSTRAP_TOKENon 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.
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.
| Value | Label | Means |
|---|---|---|
invite | By invitation only | Only someone holding an invitation link can sign up. This is the default. |
open | Anyone who can reach this address | Anybody who can open the sign-in page can create their own login. |
closed | Nobody — you create every user | Self-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.
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.
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.
#fragment removed. Register that.
Work out your value in three steps
- 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.
- 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. - 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.comandhttps://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 TestVibe | Register 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/ |
: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.
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.
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
- 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.
- Set the redirect URI to the value from section 03, and add every address your users use.
- 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.
- Copy the client ID and secret into Console → Single sign-on, into the fields named below. For OIDC, also paste the issuer.
- 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 Connect | GitHub | ||
|---|---|---|---|
| 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.
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:
curl -fsS https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration | head -c 300
Provider-specific traps
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.
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.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.
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.comafter 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.
Troubleshooting sign-in
| Symptom | Almost always | Fix |
|---|---|---|
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
- The setup checklist — the rest of the first-hour configuration.
- Licensing · AI providers · Storage · Integrations · Capacity.
- support@testvibe.com — tell us the provider, the exact error string it showed, and the address in your users' address bar. Those three answer it almost every time.