Personal access tokens
Long-lived bearer tokens for CI and external integrations. Scoped per resource group, scoped per workspace.
PATs are how non-browser, non-OAuth callers authenticate against the VibeHost API. CI pipelines, custom scripts, integrations that can't run an OAuth dance — they all use PATs.
PATs are created and managed from the dashboard only. The PAT management API itself is browser-session-only (denyPAT: true), so a PAT cannot mint more PATs — limiting blast radius if one leaks.
Create a PAT
Open vibehost.com/account/tokens → Create token. The dashboard asks for:
- Name — identifies the token in audit logs and the token list.
- Scopes — pick the minimum (see below). Leave empty for full-access (equivalent to the dashboard session).
- Resources — optionally restrict to specific app IDs.
- Expiry — 30, 60, 90, 365 days, or never.
The plaintext token (vh_pat_…) is shown exactly once on creation. The server only stores sha256(plaintext); there is no recovery. Copy it to your secrets manager immediately.
Scopes
Scope names follow <resource>:<verb>. Pick the minimum. The full list (from packages/shared/src/pat-scopes.ts):
| Scope | What it unlocks |
|---|---|
apps:read | Read app metadata, list deployments, read logs |
apps:write | Create / rename / delete apps |
apps:deploy | Upload deploy artifacts, promote, rollback (the most common CI scope) |
domains:read | List custom domains |
domains:write | Add + verify + remove custom domains |
env:read | List environment variables |
env:write | Set + remove environment variables |
members:read | List team + workspace members and per-app grants |
members:write | Invite + remove members, add + remove per-app grants |
billing:read | Read current plan + usage |
workspace:read | Read workspace settings |
workspace:write | Update workspace settings |
Notes:
- No
billing:write— billing mutations (subscribe, change plan, cancel) go through Stripe-hosted checkout, which re-authenticates against Stripe directly. Long-lived API tokens shouldn't bypass that. apps:deployis a distinct scope fromapps:writebecause deploying is the most common CI action and is materially different from create/rename/delete.- PATs created with empty scopes are full-access — equivalent to the dashboard session. Restrict by picking specific scopes.
A PAT for "GitHub Actions that deploys my-app to production" typically needs only apps:deploy + apps:read, restricted to that specific app. Less surface area = smaller blast radius if leaked.
Scoped PATs are env-gated during rollout. If SCOPED_PAT_ENFORCEMENT_ENABLED
is off on your instance, the API returns SCOPED_PAT_DISABLED when you
try to create a scoped (non-full-access) PAT. On api.vibehost.com this is
enabled. Self-hosters flip the env var once they've audited their
PAT-callable surface (see Hard Rule #14).
Resources
The "resources" field restricts a PAT to a specific set of resource IDs:
{
"apps": ["app_abc123", "app_def456"]
}A token with that resources field can only act on those two apps. Calls to other apps return 403 TOKEN_RESOURCE_MISMATCH.
v1 only enforces the apps key. domains and env keys are accepted in the schema for forward-compat but don't gate yet.
Workspace + team binding
Every PAT is bound to one workspace at issue time — the workspace you were in when you clicked Create. Calls to URLs under a different workspace return 403 TOKEN_WORKSPACE_MISMATCH.
Similarly, a PAT can optionally be team-bound. Calls to URLs under a different team return 403 TOKEN_TEAM_MISMATCH.
(These are the documented exceptions to Hard Rule #8 — the token holder already knows their token's bound scope, so the 403 code reveals nothing an attacker without the token could learn.)
Use a PAT
curl -H "Authorization: Bearer vh_pat_..." \
https://api.vibehost.com/api/v1/workspaces/<id>/appsOr with the CLI:
export VIBEHOST_TOKEN=vh_pat_...
vibehost app listThe CLI reads VIBEHOST_TOKEN if set; otherwise it uses the device-flow token in ~/.config/vibehost/config.json.
Rotation
PATs don't auto-rotate. Recommended:
- Create a new PAT with the same scopes + resources.
- Roll your secrets manager / CI to the new PAT.
- Revoke the old PAT after one deploy cycle confirms the new one works.
Revoking is dashboard-only too — open vibehost.com/account/tokens, click Revoke on the row. Revoked PATs immediately stop authenticating, but the row survives for audit (with deletedAt set).
Audit
Every PAT-authenticated request lands in the audit log with the PAT's id and name. View at vibehost.com/account/audit.
If you suspect a leak, revoke immediately, then review the audit log for unexpected actions.
See also
- Hard Rule #4 — secrets handling
- MCP server — OAuth alternative for interactive agents
- API reference — every endpoint a PAT can call