This is a personal proof-of-concept project. It is not intended for production use. Please refrain from using it.
Skip to content

Editions, organizations & BYOK

Upliftr is AI debugging agents that debug your app: you chat with them in plain English, they drive a real browser to reproduce the problem, and they root-cause the failure to the exact backend line. They are read-only by design, investigating and diagnosing across your flow and your servers without ever mutating your code, data, or infrastructure. It runs in two editions from one codebase, chosen with UPLIFTR_DEPLOYMENT_MODE:

Self-hosted (selfhosted)Cloud (cloud)
OrganizationsOne, created at first runMany, one per signup
SignupClosed (first-run setup screen)Open self-service
AI keyThe org's BYOK key, or the instance ANTHROPIC_API_KEYEach org must BYOK its own key
BillingLicense (no metered billing)License (no metered billing)

Every row of data, suites, runs, issues, plans, settings, members, is scoped to an organization, and the two are fully isolated: a user in one org can never see or touch another's. A user belongs to exactly one org.

Roles

RoleCan
ownerEverything; created automatically as the first user of an org.
adminManage members, invites, settings, the org name, same as owner for day-to-day.
memberChat with the agents, save and re-run checks, triage issues, not org administration.

The three roles above are scoped to one organization. There is one tier above them:

Platform super-admin

A platform super-admin manages every organization and user across the whole instance (cross-tenant) — the operator's console. It's a flag on a user, not an org role, and it's provisioned only from the environment, never through signup or invites. Available in both editions.

Set all three (the org is created for the admin itself; defaults to Platform):

bash
UPLIFTR_PLATFORM_ADMIN_EMAIL=manage@yourco.com
UPLIFTR_PLATFORM_ADMIN_PASSWORD=a-strong-password
UPLIFTR_PLATFORM_ADMIN_ORG=Platform

On every boot this is authoritative and idempotent: it creates the account (in its own org) or updates an existing user with that email — setting the flag, verifying it, and resetting its password to the env value (which also rotates any existing sessions). So you rotate the password by changing the env var and restarting, not in the UI.

Signed in as a platform admin, a Platform item appears in the sidebar → a console to search all users and orgs, change roles, reset passwords, force-logout, grant/revoke platform access, and delete users or whole organizations. The /api/v1/platform/* API enforces the same flag. Lock-out guards prevent deleting your own account, removing your own platform access, or deleting your own org.

Self-hosted first run

A fresh self-hosted instance has no users. Open the dashboard and you get a one-time "Create your admin account" screen, that creates the single org and its owner. Or pre-seed it headlessly:

ini
UPLIFTR_DEPLOYMENT_MODE=selfhosted
UPLIFTR_ADMIN_EMAIL=admin@yourco.com
UPLIFTR_ADMIN_PASSWORD=change-me-now
UPLIFTR_BASE_URL=https://upliftr.yourco.com   # used in invite links

Signup is closed; you grow the team with invites.

Cloud signup

In cloud mode, anyone can self-serve at the signup screen: email + password + organization name creates a new, isolated org with that user as its owner. Signups are rate-limited per IP. Each org then sets its own AI key (BYOK).

Inviting your team

An admin invites by email + role from Settings → Members:

  1. Invite, creates a tokenised link (/accept-invite?token=…) that uses UPLIFTR_BASE_URL. Send it to the person (the dashboard shows it; email delivery is up to your proxy/SMTP).
  2. Accept, they open the link, see "You've been invited to {org} as {role}", set a password, and they're in, as a member of your org.

Invites expire after 7 days, are single-use, and are listed (and revocable) under Settings → Members. A person who already has a Upliftr account elsewhere can't be invited (one user → one org); use a different address.

BYOK (bring your own key)

Upliftr never pays for your AI usage, each org brings its own Anthropic key. Set it under Settings → AI (the onboarding flow prompts for it on first run). It's encrypted at rest and, critically, threaded per-run in memory, it is never written to the shared process environment, so concurrent runs of different orgs can never see each other's key. Tracker (GitHub/GitLab/Jira) and log-backend credentials work the same way: per-org, isolated.

In selfhosted mode, an org with no key falls back to the instance ANTHROPIC_API_KEY (handy for a single-tenant box). In cloud, each org must set its own.

Audit log

Every security-relevant action, logins, signups, member and invite changes, session revocations, settings edits, org renames, is recorded per org. Admins see it under Settings → Audit log (or GET /api/v1/audit). Secret values are never logged (only which keys changed).

Configuration

VariableDefaultPurpose
UPLIFTR_DEPLOYMENT_MODEcloudcloud (open signup, many orgs) or selfhosted (one org, closed signup).
UPLIFTR_BASE_URLdashboard originPublic URL used to build invite links. Set to your domain in prod.

The dashboard reads GET /api/v1/config (public) at startup to adapt the UI to the edition. See Settings & env and Authentication.

Running the cloud edition: the hardening gate

Data isolation and per-org BYOK make the data layer safe for multi-tenant use. The cloud edition also executes a tenant's instructions (an LLM driving a real browser) on shared infrastructure, two extra risks a single-tenant box doesn't have. Both have built-in controls; turn them on for cloud:

  1. Egress isolation (SSRF). A run fetches whatever URL the tenant points it at, including, on cloud infra, the metadata endpoint (169.254.169.254) and internal services. Set UPLIFTR_RUN_EGRESS=restricted: outbound requests (browser navigations and server-log fetches) that resolve to loopback / link-local / metadata / private ranges are rejected, with a UPLIFTR_RUN_EGRESS_ALLOW allowlist. It's mode-aware on purpose, self-hosted stays open so it can test internal/localhost apps.
  2. Per-run execution isolation. Set UPLIFTR_RUN_EXECUTOR=subprocess: each run executes in a killable child process with no database access (it gets only the suite, the org's key, and an output dir), with a hard UPLIFTR_RUN_TIMEOUT_S wall-clock kill and optional memory/CPU caps. So a tenant's run can't read another's data or run forever.

For horizontally-scaled cloud, a container / Kubernetes-Job / Fly-Machine executor (one ephemeral, network-isolated container per run) plugs in behind the same executor interface, that step is tied to your platform, but the seam and the single-host isolation above are in place. Before opening cloud to untrusted tenants, also run a load + security test on your real infrastructure.

Self-hosted (one trusted org, your own network) is unaffected and secure as shipped.

Upliftr · AI-native, self-healing E2E testing. Terms · Privacy · Legal Notice