VibeHost
Guides

Concepts

Workspaces, teams, apps, deployments, channels, grants — the model behind VibeHost.

VibeHost has a small number of moving parts. Internalize this page and the CLI / dashboard / API surface will feel obvious.

The shape of an account

graph TD
  W["Workspace<br/>billing + admin boundary"] --> T1[Team A]
  W --> T2[Team B]
  W --> A1["App: my-site"]
  W --> A2["App: my-app"]
  A1 --> C1["channel: production"]
  A1 --> C2["channel: pr-42"]
  C1 --> D1["Deployment depl_abc<br/>healthy, current"]
  C1 -.older.-> D2["Deployment depl_xyz<br/>healthy, superseded"]
  C2 --> D3["Deployment depl_def<br/>healthy, current"]
  T1 -.grant: deployer.-> A1
  T2 -.grant: viewer.-> A2
  style W fill:#f4d8c5
  style A1 fill:#d8e8f5
  style A2 fill:#d8e8f5
  style C1 fill:#fff4d8
  style C2 fill:#fff4d8

A few things this diagram does not say but you'll feel anyway:

  • Apps are owned by the workspace, not by a team. A team's relationship to an app is granted, not owned.
  • Teams are how you group humans, not how you group apps. They exist so you can grant access to many apps with one grant.
  • A user can belong to multiple workspaces and multiple teams within each.
  • Rolling back doesn't delete anything. The previous deployment keeps its immutableUrl forever — rollback just moves the channel alias.

Workspaces

The billing and admin boundary. A workspace owns one plan, one bill, one set of platform-admin overrides. It also owns every app, team, deployment, and audit log row inside it.

Workspace member roles:

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

A user can belong to multiple workspaces — switch with vibehost whoami to inspect, or pass --workspace to any command.

Teams

A way to group members inside a workspace. Teams exist so you can issue one grant ("the Web team can deploy to all marketing apps") instead of N email grants.

Team member roles:

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

For 1–3 person workspaces, one team is usually enough. Add more when you want grant isolation between sub-groups (e.g. contractors vs full-time, web vs mobile).

Apps

One project, one deploy target. Created with the following — the default runtime is static:

vibehost app create my-site

App name rules: [a-z][a-z0-9-]*, 2–40 chars, unique within workspace.

The name is the app's immutable unique slug — it appears in URLs (my-site-acme.vibehost.space) and never changes after creation. For humans, an app also carries two optional display-only fields:

  • displayName (1–100 chars) — what the dashboard shows; falls back to the slug when unset.
  • description (1–500 chars) — a short blurb so you can tell apps apart at a glance.

Both are mutable and non-unique — change them any time without touching a URL:

vibehost app create my-site --display-name "Acme Marketing Site" --description "Landing pages for the spring campaign"
vibehost app update my-site --display-name "Acme Site v2"

Optionally tag an app with a primary team — surfaces in the dashboard as "owned by the Web team", but doesn't gate access by itself. Access is gated by grants (next section).

Deployment lifecycle

sequenceDiagram
  actor You as You / agent
  participant CLI
  participant API as api.vibehost.com
  participant Storage as Asset storage
  participant Edge as Edge runtime
  You->>CLI: vibehost deploy ./dist
  CLI->>CLI: build (already done) + tarball + validate
  CLI->>API: POST /deployments (chunked blobs)
  API->>API: dedup against existing blobs
  API->>Storage: assemble release
  API-->>CLI: depl_abc { url, immutableUrl, status: pending }
  API->>Edge: update channel alias
  Edge-->>API: alias active
  API-->>CLI: status: healthy
  CLI-->>You: print URL
  Note over You,Edge: From tarball POST to healthy URL: ~3–8s

What "client build" really means: the build step (npm run build) runs on your machine. The server validates, dedups, and assembles — no npm install or framework build inside our API. This is the only way the platform scales.

Channels

Preview deploys are channels, not git branches. The server has zero git knowledge.

vibehost deploy --channel production
vibehost deploy --channel pr-42
vibehost deploy --channel dark-mode-experiment

Every channel gets its own alias URL. Each channel runs independently — a deploy in dark-mode never affects production.

flowchart LR
  subgraph Channels
      Prod["production<br/>my-site.vibehost.space"]
      PR42["pr-42<br/>my-site-pr-42.vibehost.space"]
      Dark["dark-mode<br/>my-site-dark-mode.vibehost.space"]
  end
  Prod -.alias.-> A[depl_001]
  PR42 -.alias.-> B[depl_042]
  Dark -.alias.-> C[depl_dark]
  D[depl_old] -.superseded.-> Prod

Promote between channels without re-uploading:

vibehost promote depl_042 --to-channel production --app my-site

The artifact at depl_042 doesn't re-upload. Only the production alias moves to point at it.

Grants — how access actually works

To do anything on an app, a user needs an app-level grant. Grants are additive: if both your team and your email have grants, the effective role is the max.

Two grant types:

  • Team grant(app, team, role). Every member of the team picks up the role.
  • Email grant(app, email, role). Targets one person; the email need not match an existing user (invite-before-signup works).

App-level roles:

RoleCan do
viewerView deployments, see logs
deployerAbove + push new deploys, promote, rollback
adminAbove + manage grants, settings, custom domains

How a request is gated

Every viewer request runs through the platform's authz check. Password is the only true AND-gate — when set, it applies to every path. Share links bypass visibility/grant, but NOT password — a valid share-link cookie or ?share=<token> URL param satisfies the share-link gate (skipping visibility/grant), but the password gate still applies if it's set. See Grants and visibility for the full ordered walkthrough.

flowchart TD
  Req[Visitor request] --> ShareCk{"Valid share-link cookie<br/>or ?share= token<br/>+ share row still active?"}
  ShareCk -- yes --> Pwd{Password gate set?}
  ShareCk -- no --> Vis{Visibility?}
  Vis -- public --> Pwd
  Vis -- workspace --> WMem{Is workspace member?}
  Vis -- private --> Grant{Has grant?}
  WMem -- yes --> Pwd
  WMem -- no --> Deny401["401 challenge"]
  Grant -- yes --> Pwd
  Grant -- no --> Deny401
  Pwd -- no --> Allow[Serve content]
  Pwd -- yes --> PwOk{"Correct password<br/>or cookie?"}
  PwOk -- yes --> Allow
  PwOk -- no --> Deny401
  style Allow fill:#d4f4d4
  style Deny401 fill:#f4d4d4

This is why "make it public" alone doesn't unlock an app that also has a password set — the password gate is its own AND-composed layer that applies to every path, including the share-link path. The share-link bypass on visibility/grant is the explicit exception: minting a share link IS the operator's consent that "anyone with this URL can pass the visibility/grant check", but it never overrides a password.

See Grants and visibility for the full model and decision tree.

Deployments

An immutable artifact + a runtime config. Every deploy gets:

  • id — opaque deployment ID
  • immutableUrl — permanent URL that always points to this exact version
  • url — alias URL that moves with the live deployment on this channel

Rolling back doesn't mutate or delete the old deployment — it just points the channel alias at the older immutableUrl. The previous deploy keeps its own URL forever (until you app delete or the workspace is pruned).

Old deployments are pruned by vibehost gc (owner/admin only, default keeps last 5 per channel).

Personal access tokens

Long-lived bearer tokens for CI and external integrations. Scoped per resource group, optionally bound to a specific workspace and team, optionally bound to specific app IDs.

See Personal access tokens for the full scope list and rotation guidance.

How the pieces talk

graph LR
  subgraph Clients
      You[You / CLI]
      Agent[Agent / MCP]
      CI[CI / PAT]
      DB1[Dashboard]
  end
  subgraph ControlPlane["Control plane"]
      API[api.vibehost.com/api/v1]
  end
  subgraph EdgeRuntime["Edge runtime"]
      Edge[Tenant router]
      Static[Static assets]
  end
  You --> API
  Agent --> API
  CI --> API
  DB1 --> API
  API -.assets.-> Static
  Static --> Edge
  Edge --> Visitor(("*.vibehost.space<br/>visitor"))

The control plane handles API + dashboard; tenant apps run in isolated edge sandboxes — no shared process. Everything goes through the same REST API; CLI, MCP, and dashboard all call the same endpoints with the same auth checks.

Naming cheat sheet

TermWhat it meansCommon confusion
WorkspaceThe billing boundary; owns apps + teamsNot "team" — a team is a group of humans inside the workspace
TeamA grant target — give one team access to many appsDoesn't own apps; just receives grants
AppOne deploy target (one runtime, one set of channels)Not "project" — sometimes one product is multiple apps
ChannelA named slot for a deployment (production, pr-42)Not a git branch — server has zero git knowledge
DeploymentAn immutable artifact + its immutableUrlSurviving rollback is by design, not a quota leak
GrantPer-app role given to a team or an emailComposes with visibility / password; never short-circuits
Visibilitypublic / workspace / private — first access gateNot an alternative to grants; both apply
Share linkA cookie-issuing URL for time-bound public-ish accessDifferent from "public" — individually revocable, expirable

On this page