Machines and pairing
How a daemon proves itself to a workspace. Per-org identity, directory binding, and the pair → bind → stream contract.
A machine in Viewport is a daemon paired to an organization. The pairing is the trust relationship between your laptop and a tenant. Per-org pairings are cryptographically isolated so the same physical machine can serve multiple orgs without any cross-correlation.
The three commands
vpd install → daemon installed, no remote streaming
vpd pair → org credentials issued, still no directory streaming
vpd bind → this directory streams to this org's relayThree discrete trust grants. Each one is opt-in, each one is reversible.
This is the privacy-first model. Pairing does not imply streaming. You can pair a laptop to your work org and have zero directories binding to it, in which case the daemon will not ship anything to that org's relay.
Pairing
vpd pair runs an interactive flow:
- Daemon
POSTs/api/pairing-codesto mint a fresh 8-character code. - Daemon prints a browser URL:
app.getviewport.com/pair?code=…. - You open the URL, sign in, pick which workspace to pair into, click Approve.
- Platform stores: a new install row, a runtime target row, a machine row, all in that workspace.
- Platform issues a per-binding relay credential and writes it back via the pairing flow.
- Daemon persists the credential into
~/.viewport/config.json(mode 0o600) and reconnects to the relay.
Pairing produces, per org binding:
| Field | Purpose |
|---|---|
workspaceId | The org this binding belongs to. |
installId | Stable identifier for this daemon install in this org. |
runtimeTargetId | The runtime row the platform sees. |
machineId | This org's view of this machine. Different in different orgs. |
issueToken | Relay credential for this binding. |
identity | Per-binding keypair, separate file on disk. |
Calling vpd pair again with a different org runs the same flow and produces a second binding alongside the first. The two bindings cannot see each other's state.
Directory binding
Pairing alone does not stream. Sessions in a directory only stream if the directory is bound to an org.
vpd bind ~/work/api --org 01J7…This writes ~/work/api/.viewport/local.yaml (gitignored):
org_id: 01J7M8N9P0Q1R2S3T4U5V6W7X8
streaming: enabled
bound_at: "2026-05-11T14:42:00Z"On session start, the daemon resolves the nearest local.yaml walking up the directory tree. If found and streaming: enabled, the session is pinned to that org binding for its entire lifetime. If not found, the session runs local-only. The agent works, but nothing leaves the machine.
The committed workspace.yaml hint
.viewport/workspace.yaml is a different file. It's committed to the repo and contains:
org_id: 01J7M8N9P0Q1R2S3T4U5V6W7X8It's a hint, not authoritative. When you cd into a repo with this file:
- If you have a binding for that org → daemon prompts: "Bind this directory to Acme Engineering? [Y/n]".
- If you don't → daemon shows: "This repo suggests org
acme, but you're not paired to it. Runvpd pair --org acme." - If you say no → daemon caches the decline so it doesn't ask again on every
cd.
The hint cannot grant streaming on its own. Streaming always requires local.yaml.
Session pinning
The binding is resolved once, at session start. Mid-session changes. Revoking the binding, swapping local.yaml, switching orgs in the web UI. Do not migrate the session to a new org. The session either:
- Continues streaming to the original org until it ends, or
- Gets cut (remote streaming stops, local session continues) if the binding is revoked.
Already-streamed frames stay in the original org's records. Nothing is replayed elsewhere. This invariant exists because mid-session migration would create an audit-trail gap that no compliance story survives.
Machine identity per binding
The daemon's per-org identity is separate. Each binding has:
- A separate Ed25519 keypair (file:
~/.viewport/relay-identities/{workspaceId}.json, mode 0o600). - A separate
machineIdissued by the platform. - A separate
runtimeTargetId.
This means an admin in org A who reads their machines table cannot correlate any row there with any row in org B's machines table. Even if the same physical laptop is paired to both, the cryptographic and identifier surface for each org is independent.
Revocation
You can revoke a pairing from either side.
From the web app: Machines → row → Revoke. The platform marks the runtime target revoked_at. On next reconnect, the relay refuses the JWT for that binding. The daemon detects the rejection, deletes the binding from its local config, and stops streaming for that org. Active sessions for that org stop remote streaming (local sessions continue).
From the daemon: vpd unpair --org 01J7…. Same end state. Useful when revoking from the platform side isn't reachable (offline machine).
When a member is removed from an org, the platform revokes all that user's machine pairings in that org. Their other org bindings on the same machine are untouched.
Multi-org daemons today
A single daemon can hold multiple bindings simultaneously. Each binding runs its own relay bridge, its own reconnect logic, its own session routing. vpd status shows one row per binding with its own health.
$ vpd status
Daemon: mehr-mbp · v0.2.1 · running
Org bindings (3):
Acme Engineering ws://relay.acme.example/ws connected
Personal ws://relay.getviewport.com/ws connected
OSS Maintainers ws://relay.getviewport.com/ws reconnecting (1/5)
Bound directories (4):
~/work/api → Acme Engineering
~/work/web → Acme Engineering
~/personal/... → Personal
~/oss/relay → OSS MaintainersCommon patterns
Pattern: solo consultant with 3 clients. Pair to each org once. vpd bind ~/clients/acme --org 01J... --recursive, same for the others. Each client repo automatically streams to its own org.
Pattern: enterprise BYOA (bring your own agent). Pair the corporate laptop to the org once. vpd bind ~/code --recursive. Done. The daemon stays paired across reboots; pairings survive vpd update.
Pattern: shared dev box. Don't. Pairings are per-user, not per-machine. A shared box that pairs into one user's org makes the audit trail wrong. Pair per individual.
Where to go next
- Concepts: Trust and privacy.
- Developer quickstart. Pair your first machine.
- Add a repo. Directory binding in practice.
- Self-host the relay. Change where pairings connect to.