VibeHost
Guides

Workspaces & teams

Workspace = billing/admin boundary + owns apps. Team = member sub-group inside the workspace. Grants link teams to apps.

The model

Workspace ─── billing + admin boundary, owns apps
  ├── plan (Free / Pro / Enterprise)
  ├── billing contact, invoices
  ├── workspace-level audit log
  ├── Workspace members ──► role: owner / admin / member
  ├── Teams ──► sub-groups of members
  │     └── Team members ──► role: owner / manager / member
  └── Apps ──► owned by the workspace
        └── App-team grants ──► viewer / deployer / admin

The thing this diagram does not show is "apps belonging to a team" — because they don't. Apps belong to the workspace. A team's relationship to an app is granted, not owned. This matters when you're thinking about access:

  • A workspace admin sees all apps in the workspace.
  • A team member sees only the apps that the team (or their email) has been granted.

Workspace member roles

RoleWhat it unlocks
memberBe in the workspace; see what app-level grants give you
adminCreate teams, manage workspace settings, see all apps
ownerPlus billing, plan changes, workspace deletion

You can belong to multiple workspaces (e.g. personal + work). The CLI defaults to the workspace stored in ~/.config/vibehost/config.json — switch with vibehost workspace use <slug>.

When to add a team

For a 1–3 person account, one team is usually enough. Add more when:

  • You want grant isolation: members of Team A shouldn't pick up app grants from Team B.
  • You're modeling org structure: each team mirrors a real-world group (squad, department, contractor).
  • You want to share many apps with one grant: "everyone on the Web team can deploy to all marketing apps" is one grant per app on the team, not N grants per email.

The slug here (web) must be DNS-safe and lowercase. vibehost team switch web makes it the current team, and vibehost team info prints the current team's settings.

vibehost team create web
vibehost team ls
vibehost team switch web
vibehost team info

Team slugs appear in every generated app URL (<app>-<slug>.vibehost.space), so pick a short DNS-safe one.

Team member roles

RoleWhat it unlocks
memberBe in the team; receive whatever app grants the team has
managerManage team members + team settings
ownerPlus delete the team

These are roles within the team. They don't grant anything on apps by themselves — the team has to receive an app grant separately (see Grants and visibility).

Inviting people

Invites work at two layers — workspace and team — and the right one depends on how broad you want the access to be.

Invite to the current workspace (workspace owner/admin only). Use this when the invitee's email isn't covered by the workspace's allowed-domain list. It prints a single-use link. The --role is admin or member:

vibehost workspace invite teammate@x.com --role member

Invite to the current team. This prints a sign-in link to paste into Slack/email. Only the invitee's email can accept; forwarded links are rejected server-side. The --role is member or manager:

vibehost team invite teammate@x.com --role member

If your workspace has a verified domain (see below), users matching that domain auto-join — you don't need to invite them individually.

Linking teams to apps

A team gaining access to an app is a separate operation from team membership. Use app grants:

vibehost app grants add-team <team-slug> deployer --app my-site

Now every member of the team has the deployer role on my-site. Add more grants for more apps; revoke per-app with app grants remove-team.

For one-off invites without a team, grant by email:

vibehost app grants add-email contractor@x.com viewer --app my-site

Workspace-level actions

ls lists the workspaces you belong to, use <slug> switches the active workspace, info prints the current workspace detail, members shows who's in this workspace, and role <email> <role> changes a member's role.

vibehost workspace ls
vibehost workspace use <slug>
vibehost workspace info
vibehost workspace members
vibehost workspace role <email> <role>

Workspace verified domains (auto-join)

For company workspaces, add a verified domain so anyone signing up with a matching email auto-joins:

vibehost workspace domain add prints a DNS TXT record. Add it at your registrar, then run vibehost workspace domain verify <id>:

vibehost workspace domain ls
vibehost workspace domain add yourcompany.com --tier verified
vibehost workspace domain verify <id>

Two tiers:

  • verified — DNS TXT proves the workspace owns the domain. Matching users auto-join at the member role.
  • member — no DNS, must match an existing member's domain. Matching users see a prompt to join, no auto-join.

Audit log

Every mutation on a workspace, team, or app (grants, deletes, password changes, custom-domain attach, role updates, self-grants) writes to a workspace-scoped audit log. Workspace owners + admins can read it:

Plain vibehost audit returns the latest 100 rows; --since 7d --json is handy for analytics / external SIEM; --actor filters by actor.

vibehost audit
vibehost audit --since 7d --json
vibehost audit --actor teammate@x.com

Each row carries: actor, action, targetType, targetId, before, after, at. We never delete audit rows; retention is workspace-lifetime.

URL scope

Every team-scoped and workspace-scoped API route lives under explicit URL params:

/api/v1/workspaces/:workspaceId/...
/api/v1/workspaces/:workspaceId/teams/:teamId/...

The CLI derives :workspaceId / :teamId from ~/.config/vibehost/config.json and refuses to call the API if they're missing — that's how it knows which workspace to target.

For PAT callers: PATs are bound to a specific workspace (and optionally a specific team) at issuance time. Hitting another workspace's URL with the wrong PAT returns 403 TOKEN_WORKSPACE_MISMATCH (or 403 TOKEN_TEAM_MISMATCH). These dedicated codes are an intentional exception to the otherwise-unified "access denied" message: the token holder already knows their token's bound scope, so the code carries no information an attacker without the token could obtain.

Common gotchas

  • Apps are unique per workspace — apps are owned by the workspace; the per-app "primary team" tag is just optional metadata.
  • Team slugs appear in URLs — picking a short DNS-safe slug matters; rename with vibehost team rename-slug <new-slug> (the slug stays stable when the team's display name changes, by design).
  • PATs are bound to one workspace — a PAT created in workspace A can't call workspace B's API even if you also belong to B. Create separate PATs per workspace. (See TOKEN_WORKSPACE_MISMATCH.)
  • A workspace admin doesn't auto-pick up team membership — admins see all apps, but actions that check team-based grants still require the admin to be a team member. (Usually a non-issue because workspace admins are also team members of every relevant team.)
  • Last owner can't be removed. Trying to demote / delete the last owner of a workspace or team returns 409 LAST_OWNER. Promote someone else first.
  • vibehost workspace delete refuses if apps still exist or if other members are present. Delete (or transfer) apps first, remove members, then delete the workspace.

On this page