Viewport daemon, relay, and hosted runtime are available in alpha. Surfaces may change.
VIEWPORT
Start

Self-serve first run

Sign up, connect GitHub and Slack, pair a self-hosted worker, commit .viewport policy, and prove the first governed agent run.

This is the launchable early-partner path. It assumes hosted Viewport, one self-hosted polling worker, GitHub, Slack, and one sandbox repository.

After the run works once, use the Launch checklist as the team acceptance bar before inviting more users.

The goal is one visible governed run:

Slack or GitHub event
  -> .viewport route
  -> git-backed policy
  -> self-hosted vpd worker
  -> plan-only agent
  -> human approval
  -> implementation agent
  -> bounded branch + PR
  -> Slack completion
  -> receipts and usage

1. Create The Workspace

Open the hosted app and create your organization:

https://app.getviewport.com

Create one team for the first proof. Assign at least one reviewer tag that your policy will use, for example tech-lead. Viewport rejects policy sync when a human gate points at a reviewer tag that has no matching team member, so fix tag membership before triggering the first run.

2. Connect GitHub

Install the Viewport GitHub App on a sandbox repository.

The proven first-run path needs:

  • contents read/write for checkout and branch publish;
  • pull request read/write for the PR side effect;
  • webhook delivery to Viewport;
  • JSON webhook body.

Use one safe repo first. Do not start with production repos or broad organization-wide installation scope.

3. Connect Slack

Connect Slack and choose the channel for completion receipts.

Slack is a notification surface. Viewport run detail remains the source of truth for approvals, usage, branch/PR receipts, and audit packets.

If the first route starts from Slack, use one controlled launch channel and verify that completion replies to the source thread. A seeded or fixture run may post directly to the configured channel because it has no source Slack timestamp.

4. Install And Pair vpd

Install vpd on the machine that should execute the work:

npm install -g @viewportai/daemon
vpd --version

Pair it as a self-hosted worker. Hosted Viewport is the default server:

vpd pair PAIRING_CODE --worker --transport=polling --workdir "$HOME/.viewport/worktrees"

For a customer-internal or self-hosted compatible control plane, make the server explicit:

vpd pair PAIRING_CODE --worker --server https://viewport.customer.internal --transport=polling --workdir "$HOME/.viewport/worktrees"

Then run doctor:

vpd worker doctor --json

Doctor should show vpdProfile, the paired server, workspace root, worker identity, transport polling, workspaceId, and detected capabilities. workspaceId should match the workspace where you generated the pairing code. If you use --profile, vpdProfile.home should be the profile directory you expect before you start the worker.

The workdir is recorded during pairing. If you change it later, reset and pair again so vpd worker start uses the intended workspace root.

5. Commit .viewport/

Declarative Viewport config is committed to Git. It is not local daemon state.

Minimum layout:

.viewport/
  policy.yaml
  routes/
    github-pr.yaml

Run this before pushing:

vpd check .

vpd check validates policy and route syntax. It also warns if .viewport/ is gitignored, which would prevent the control plane from reconciling your policy.

Minimal policy.yaml:

version: 1
repos:
  - repo: acme/sandbox
    access: read-write
    credential: brokered
    branches:
      push_allowed: ["agent/**"]
      restricted: ["main"]
invoke:
  agent: claude-code
  notify:
    provider: slack
    credential_ref: slack/oauth
    channel: C1234567890
gates:
  - name: plan-review
    type: plan
    reviewers:
      tags: ["tech-lead"]
    timeout: 4h
    on_timeout: escalate

Minimal routes/github-pr.yaml:

route:
  name: github-pr-review
  team: platform
  trigger:
    integration: github
    events: ["pull_request.*"]
    conditions:
      repo: acme/sandbox
  policy:
    source: git
    repo: acme/sandbox
    ref: main
    path: .viewport/policy.yaml
  priority: 20

Minimal Slack support route:

route:
  name: slack-support
  team: platform
  trigger:
    integration: slack
    events: ["app_mention"]
    conditions:
      channel: C1234567890
      mentionsAny:
        - viewport
  policy:
    source: git
    repo: acme/sandbox
    ref: main
    path: .viewport/policy.yaml
  priority: 30

6. Register The Sync Source

In the app, register the repository/path that contains .viewport/.

Healthy sync shows:

  • sync status synced;
  • latest short SHA;
  • route count;
  • no repeated HMAC failures;
  • a composed policy workflow version.

If sync is failing, fix this before triggering a run. A worker cannot execute a policy the server has not reconciled.

7. Start The Worker

Start the worker after the policy is synced:

vpd worker start --mode persistent --transport polling

Use the command shown in the app as the source of truth. New self-hosted runners use vpd pair --worker ... followed by vpd worker start ....

8. Trigger And Approve

Open or update a pull request in the sandbox repo, or mention the app in the configured Slack launch channel if your first route is Slack-backed.

Expected run shape:

checkout -> plan -> gate_plan-review -> agent -> publish -> open_pr -> notify_completion

The run should pause at the plan review gate. Approve it in Viewport. After approval, the worker resumes, implements, publishes an agent/** branch, opens a GitHub PR or provider proposal, and posts the Slack completion. For Slack-originated runs with a source timestamp, the completion should reply in the source thread.

9. Verify The Run

A good first run shows:

  • matched route and git-backed policy;
  • composed workflow version and policy hash;
  • brokered GitHub checkout;
  • plan-only node with no write/shell tools;
  • human approval with approver and timestamp;
  • implementation node usage and cost when reported by the adapter;
  • bounded branch and GitHub PR URL;
  • Slack completion permalink;
  • Slack source thread for Slack-originated runs;
  • execution receipts with provider URLs;
  • redacted action bodies and no runtime secrets in stored metadata.

Troubleshooting

Start here:

Common first-run failures:

SymptomLikely causeCheck
.viewport/ never syncsDirectory is gitignored or not pushedvpd check . and git status --ignored .viewport
Policy sync errors on reviewer tagsReviewer tag has no matching memberTeam settings reviewer tags, then sync again
Worker never claimsWorker not paired, wrong runner pool, missing capabilityvpd worker doctor --json
Worker exits with "already running"Another persistent worker is active for this server/workspace/executorStop the old worker with vpd worker stop, then restart; for proof-only execution use vpd worker run-once --lease <lease-token> --transport polling
Checkout failsGitHub App not installed or credential grant missingIntegration settings and run node error
Slack notification skippedSlack integration is disconnected, the policy channel is wrong, or the provider grant failed closedIntegration settings, invoke.notify.channel, and the Slack receipt/error on run detail
Slack posts to the channel instead of the source threadSource timestamp missing or renderer/threading regressionRun detail source event and Slack receipt permalink

Capture a sanitized support packet before asking for help:

vpd worker doctor --json > /tmp/vpd-worker-doctor.json
vpd status --json > /tmp/vpd-status.json
vpd check . --json > /tmp/vpd-check.json

Then copy the workspace id, sync source id, workflow version id, run id, route name, runner name, and any denial code shown in the app.

Do not include raw tokens, pairing codes, claim tokens, lease tokens, worker private keys, or provider credentials. See Support packet for the full handoff checklist.

On this page