Editions, organizations & BYOK
Tripwire runs in two editions from one codebase, chosen with TRIPWIRE_DEPLOYMENT_MODE:
Self-hosted (selfhosted) | Cloud (cloud) | |
|---|---|---|
| Organizations | One — created at first run | Many — one per signup |
| Signup | Closed (first-run setup screen) | Open self-service |
| AI key | The org's BYOK key, or the instance ANTHROPIC_API_KEY | Each org must BYOK its own key |
| Billing | License (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
| Role | Can |
|---|---|
owner | Everything; created automatically as the first user of an org. |
admin | Manage members, invites, settings, the org name — same as owner for day-to-day. |
member | Author and run suites, triage issues — not org administration. |
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:
TRIPWIRE_DEPLOYMENT_MODE=selfhosted
TRIPWIRE_ADMIN_EMAIL=admin@yourco.com
TRIPWIRE_ADMIN_PASSWORD=change-me-now
TRIPWIRE_BASE_URL=https://tripwire.yourco.com # used in invite linksSignup 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:
- Invite — creates a tokenised link (
/accept-invite?token=…) that usesTRIPWIRE_BASE_URL. Send it to the person (the dashboard shows it; email delivery is up to your proxy/SMTP). - 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 Tripwire account elsewhere can't be invited (one user → one org); use a different address.
BYOK (bring your own key)
Tripwire 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
| Variable | Default | Purpose |
|---|---|---|
TRIPWIRE_DEPLOYMENT_MODE | cloud | cloud (open signup, many orgs) or selfhosted (one org, closed signup). |
TRIPWIRE_BASE_URL | dashboard origin | Public 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:
- 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. SetTRIPWIRE_RUN_EGRESS=restricted: outbound requests (browser navigations and server-log fetches) that resolve to loopback / link-local / metadata / private ranges are rejected, with aTRIPWIRE_RUN_EGRESS_ALLOWallowlist. It's mode-aware on purpose — self-hosted staysopenso it can test internal/localhostapps. - Per-run execution isolation. Set
TRIPWIRE_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 hardTRIPWIRE_RUN_TIMEOUT_Swall-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.