VibeHost
Guides

Grants and visibility

How VibeHost decides who can see and act on an app. Password is the only true AND-gate; share links bypass visibility/grant but never password.

An app's effective access policy is the composition of four mechanisms. Password is the only true AND-gate — when set, every path must clear it. Share links bypass visibility/grant but NOT password — a valid share-link cookie satisfies the share-link gate (skipping visibility/grant), but if a password is set the visitor still has to enter it. The bypass on visibility/grant is the documented exception; new gates AND in by default. See How a request gets evaluated for the actual order.

The four mechanisms

        ┌──────────────────────────┐                      ┌──────────────┐
        │  Visibility ∧  Grants    │   ← bypassed by →    │  Share link  │
        │  public/ws/    team or   │                      │  (if used)   │
        │   private      email     │                      │              │
        └──────────────────────────┘                      └──────────────┘
                       │                                         │
                       └─────────────┬───────────────────────────┘

                              ┌──────────────┐
                              │   Password   │   ← true AND-gate,
                              │   (if set)   │     applies to every path
                              └──────────────┘


                              access granted

A visitor without a share link must satisfy visibility AND grant AND password (if set). A visitor with a valid share-link cookie or ?share= URL param skips visibility/grant only — if a password is set they still have to enter it. Password is the one gate that applies to every path.

Visibility

Set per-app, decides who can hit the app at all:

VisibilityWho can request
publicAnyone on the internet (no auth required)
workspaceAny workspace member
privateOnly users with an explicit grant
vibehost app visibility my-site public
vibehost app visibility workspace --app my-site
vibehost app visibility private --app my-site

Visibility alone doesn't grant action permissions (deploy, settings) — those still need a grant.

Grants

Two grant tables: team-based and email-based. Both have the same role enum:

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

Grants are additive. If both your team and your email have grants, the effective role is the max (admin > deployer > viewer).

Team grants apply to everyone in the team; email grants target one person (no team needed). grants ls lists both axes, and grants self grants your own user (handy for testing):

vibehost app grants add-team web deployer --app my-site
vibehost app grants remove-team web --app my-site

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

vibehost app grants ls --app my-site

vibehost app grants self admin --app my-site

Email grants work before the invitee signs up. The grant is held pending; on first login with a matching email, the system resolves the user.

Password gate

Optional second factor on top of visibility + grants:

vibehost app password set <password> --app my-site
vibehost app password clear --app my-site
vibehost app password status --app my-site

Once set, anyone hitting the public URL sees a password prompt. After correct entry, a signed cookie remembers them for 7 days. The password gate applies to every path — even visitors who already cleared the share-link gate must enter it.

Use case: soft launches. "Public visibility, but only people I share the password with can see it."

Generate single-use cookie-issuing URLs:

vibehost app share-link create --app my-site
vibehost app share-link ls --app my-site

Each share link is a one-time URL like https://my-site.vibehost.space/share/<token>. Visiting it sets a 24-hour cookie tied to that share link; the cookie satisfies the share-link gate (skipping visibility/grant) for that browser. If the app has a password set, the visitor still has to enter the password — share links never bypass the password gate.

Use case: client previews. "Send this link to my client; revoke it next month."

How a request gets evaluated

When a viewer hits my-site.vibehost.space/index.html, the platform runs an authz check in this order:

  1. Share-link gate. If a ?share=<token> URL param resolves to an active share link, or a share cookie checks out AND the share is still active → share-link gate satisfied; visibility/grant are skipped. (Continue to step 2 — the request is not yet allowed.) For visitors without a share token, the platform falls through to visibility + grant check: public → pass; workspace → must be workspace member; private → must have an explicit grant (team or email).
  2. Password gate. If a password is set and no valid password cookie is present → 401 password prompt. This applies to every path, including the share-link path — minting a share link does not bypass the password.
  3. Action-level role check (for write endpoints). Read → viewer+. Deploy → deployer+. Settings/grants → admin.

A valid share-link cookie satisfies the share-link gate (skipping visibility and grant). It does not skip the password gate. If you want "anyone with this URL can view" with no further friction, don't set a password on the app. If you want "anyone with this URL and this password can view", set both — that combination is supported and behaves as two AND-composed factors (link unlocks visibility/grant; password is still required).

Password is the only true AND-gate: when it's set, every path — share-link, workspace, private grant, public — must clear it. The share-link bypass on visibility/grant is the one documented exception to the AND-composition rule.

What this means in practice

SetupAnonymous (no link)Anonymous with valid share linkWorkspace member (no grant)Grant-holder
public, no password, no share linksviewn/aviewview
public, password setwith passwordwith passwordwith passwordwith password
workspace, no passwordn/aviewview
private, no extrasn/aview
private + active share linkview✗ (link required)view (link not needed)
private + password + share linkwith password✗ (no grant)with password

The model has two gate kinds:

  • Visibility / grant — share-link bypasses these. A valid share-link cookie skips the visibility check and the grant check.
  • Password — true AND-gate. When set, applies to every path including share-link. New gates AND in by default; the share-link bypass on visibility/grant is the documented exception.

Decision tree — "I want to share this app with..."

flowchart TD
  Start{"Who am I sharing with?"} --> Pub["Anyone on the internet<br/>(marketing site, demo)"]
  Start --> Team["Everyone on my team<br/>(internal tool)"]
  Start --> Sub["A specific group<br/>(contractors, web team)"]
  Start --> One["One person<br/>(client, prospect)"]
  Start --> Temp["Anyone with a link<br/>(time-bound)"]
  Pub --> PubCmd["visibility public"]
  Team --> TeamCmd["visibility workspace"]
  Sub --> SubCmd["visibility private<br/>+ grants add-team"]
  One --> OneA{"Do they have a<br/>VibeHost account?"}
  OneA -- yes --> OneCmd["grants add-email viewer"]
  OneA -- no --> OneB["grants add-email viewer<br/>(invite-before-signup works)"]
  Temp --> TempCmd["share-link create<br/>--expires 7d"]
  style PubCmd fill:#fff4d8
  style TeamCmd fill:#fff4d8
  style SubCmd fill:#fff4d8
  style OneCmd fill:#fff4d8
  style OneB fill:#fff4d8
  style TempCmd fill:#fff4d8

Common scenarios — full commands

You want the whole workspace to see the app, no one outside:

vibehost app visibility staging-tool workspace

That's it. Workspace members open the URL and see it; non-members get a 401 (or 404 if visibility doesn't even reveal app existence).

You want to send a link to a client for the next week, then auto-revoke:

vibehost app visibility client-pitch private
vibehost app share-link create --app client-pitch --expires 7d --label "Acme review"

The CLI prints a https://client-pitch-<ws>.vibehost.space/share/<token> URL. Send it to the client. After 7 days the link expires and the cookie they got from clicking it stops working.

Revoke earlier if needed:

vibehost app share-link ls --app client-pitch
vibehost app share-link revoke vhs_abc123 --app client-pitch

You're launching to the public but want a soft gate — "anyone with the password can see":

vibehost app visibility soft-launch public
vibehost app password set "shipmate-2026" --app soft-launch

Share the URL + password in your launch email. When you're ready to fully open:

vibehost app password clear --app soft-launch

Anyone signed in to VibeHost can view, but no anonymous visitors:

vibehost app visibility open-beta workspace
vibehost app grants add-team beta-testers viewer --app open-beta

(Workspace alone covers internal; add a team grant for cross-workspace beta if needed.)

Only specific people, no public discovery, no share links:

vibehost app visibility internal-tool private
vibehost app grants add-team admins admin --app internal-tool
vibehost app grants add-email auditor@acme.com viewer --app internal-tool

Optional belt-and-suspenders:

vibehost app password set "$(openssl rand -hex 16)" --app internal-tool

Three gates: visibility (private — grant required), grant check, password. All three AND-compose; loss of any one denies access.

Access matrix — quick reference

SettingAnon visitorWorkspace member (no grant)Email-grant viewerTeam-grant deployerAdmin
public✓ view✓ view✓ view✓ view + deploy✓ all
public + passwordwith passwordwith passwordwith passwordwith passwordwith password
workspace✓ view✓ view✓ view + deploy✓ all
workspace + password✓ + password✓ + password✓ + password✓ + password
private✓ view✓ view + deploy✓ all
private + share linkwith linkwith link✓ (link not needed)✓ (link not needed)✓ all
private + share link + passwordwith link + passwordwith link + passwordwith password (link not needed)with password (link not needed)with password

Reading the table: "✓" means the gate at this row + column passes; the visitor still needs to satisfy any other active gates. Password always applies when set — the share link doesn't get a visitor past it.

How to check who has access

vibehost app inspect my-site --json

The inspect response includes visibility, passwordSet, all team grants, all email grants, and active share links — the entire access surface in one call. This is the agent-friendly way to audit an app.

For a per-deployment view, use the dashboard's audit log or vibehost audit --target app:my-site --json.

Error codes you might see

CodeStatusMeaning
FORBIDDEN403You're signed in but lack the required role
UNAUTHENTICATED401No / expired session, or password gate not satisfied
NOT_FOUND404App doesn't exist OR you lack a grant that would reveal it (privacy: we don't enumerate existence to non-grantees)
INVALID_GRANT_TARGET400add-team with a slug that doesn't exist in this workspace
CROSS_WORKSPACE_GRANT400Tried to grant a team from a different workspace
SELF_GRANT_FORBIDDEN403Can't grants add-email your own email — use grants self <role> (audited) instead
ACCESS_REQUEST_PENDING409Your "request access" was already submitted; waiting on admin
ACCESS_REQUEST_COOLDOWN429Your request was denied in the last 24h — wait before re-requesting

Self-grants are audited

vibehost app grants self admin --app my-site is allowed (you might need to deploy your own app after a workspace migration), but it writes an audit row marked self_grant=true. Workspace owners can filter the audit log on this flag to spot escalation patterns.

On this page