> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinework.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Environment and device

> Environment is a field set to cloud or device. A device is a machine you connected, with a dvc_ id, a liveness status and a run limit.

Environment is a field, not an object. Its value is `cloud` or `device`, and it says where a run executes. A device is a machine you connected to the workspace. Its id is `dvc_` plus 16 letters and digits. For how Pinework picks the place, see [Cloud and your machine](/concepts/cloud-and-device).

## States

A device's state is computed from its last heartbeat, not stored. It appears in `liveness.status`.

| State | Meaning | Ends here |
| - | - | - |
| `connected` | The device sent a heartbeat within the last 90 seconds. It can take runs. | No |
| `disconnected` | No heartbeat for over 90 seconds, or none yet. Device runs wait. | No |
| `retired` | No heartbeat for over 7 days. Its sign-in no longer works. | Yes |

| From | To | Trigger |
| - | - | - |
| `disconnected` | `connected` | The device manager sends a heartbeat. |
| `connected` | `disconnected` | 90 seconds pass with no heartbeat. |
| `disconnected` | `retired` | 7 days pass with no heartbeat. |
| any | removed | You delete the device. Its token stops working. |

A phone or tablet (`ios`, `ipados`, `android`) has `liveness.kind` set to `controller`. It has no status.

## Fields

### The environment field

| Object | Field | Values |
| - | - | - |
| Agent | `environment` | `device` or `cloud`. Defaults to `device` when you create an agent. |
| Task | `environment` | `device`, `cloud`, or null to use the agent's choice. |
| Automation | `environment` | `cloud`, `device`, or null to use the agent's choice. |
| Run | `environment` | An object with `type` (`cloud` or `device`) and `cwd` (`home`, `sandbox-root` or `repo-root`). Null when the run does no file or code work. |

### Device

`GET /api/v1/devices` returns `{ "devices": [...] }`. Each device has these fields.

<ResponseField name="deviceId" type="string" required>
  The connection id, a UUID. `PATCH` and `DELETE` take this value in the path.
</ResponseField>

<ResponseField name="publicId" type="string" required>
  The durable device id, `dvc_…`. It stays the same across reconnects.
</ResponseField>

<ResponseField name="displayName" type="string" required>
  The name shown in the dashboard.
</ResponseField>

<ResponseField name="hostname" type="string">
  The machine's hostname. Null when the device never sent one.
</ResponseField>

<ResponseField name="platform" type="string">
  The OS, such as `darwin` or `linux`. Null when unknown.
</ResponseField>

<ResponseField name="version" type="string">
  The device manager version. Null when unknown.
</ResponseField>

<ResponseField name="maxConcurrent" type="integer" required>
  How many runs the device takes at once.
</ResponseField>

<ResponseField name="isWorkspaceDefault" type="boolean" required>
  True when this is the workspace default device.
</ResponseField>

<ResponseField name="liveness" type="object" required>
  The computed state.

  <Expandable title="properties">
    <ResponseField name="kind" type="string">`host` for a machine that runs work, `controller` for a phone or tablet.</ResponseField>
    <ResponseField name="status" type="string">Host only. `connected`, `disconnected` or `retired`.</ResponseField>
    <ResponseField name="expiresAt" type="string">Host only. When the current heartbeat lapses. Null before the first heartbeat.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="online" type="boolean" required>
  Legacy. True when `liveness.status` is `connected`.
</ResponseField>

<ResponseField name="retired" type="boolean" required>
  True when `liveness.status` is `retired`.
</ResponseField>

<ResponseField name="lastHeartbeatAt" type="string">
  The last heartbeat. Null before the first one.
</ResponseField>

<ResponseField name="liveUntil" type="string" required>
  The device counts as connected until this time.
</ResponseField>

<ResponseField name="providerReadiness" type="object" required>
  Which harnesses and providers are installed and signed in on the device. For display.
</ResponseField>

<ResponseField name="providerReadinessCapturedAt" type="string">
  When readiness was last checked. Null before the first check.
</ResponseField>

<ResponseField name="claimState" type="object">
  Whether the device is picking up runs, and why not. Null before the first report.
</ResponseField>

<ResponseField name="remoteControlRevokedAt" type="string">
  When you revoked this device as a remote controller. Null when not revoked.
</ResponseField>

<ResponseField name="createdAt" type="string" required>
  ISO 8601 timestamp.
</ResponseField>

<ResponseField name="updatedAt" type="string" required>
  ISO 8601 timestamp.
</ResponseField>

### Folder

A folder is a directory on a device that runs can work in. Its id is `fs_` plus 16 letters and digits.

<ResponseField name="shortId" type="string" required>
  The folder id, `fs_…`.
</ResponseField>

<ResponseField name="displayName" type="string" required>
  The folder's name.
</ResponseField>

<ResponseField name="config" type="object" required>
  `deviceId` (the `dvc_…` id), `rootPath` (an absolute path) and `bindingState` (`active` or `manual_rebind_required`).
</ResponseField>

<ResponseField name="status" type="string" required>
  `active`, `disconnected` or `revoked`.
</ResponseField>

<ResponseField name="repoId" type="string">
  The repo this folder holds. A device run for that repo starts here. Null when not set.
</ResponseField>

<ResponseField name="createdAt" type="string" required>
  ISO 8601 timestamp.
</ResponseField>

## CLI

| Command | Does |
| - | - |
| `pinework device connect` | Starts the device manager in the foreground, or reports the one running. |
| `pinework device disconnect` | Stops the device manager. Refuses while runs are active. |
| `pinework device status` | Shows this machine's device manager state, connection and active runs. |
| `pinework device set --max-concurrent <n>` | Sets a device's run limit. `--device` targets another of your devices. |
| `pinework device refresh` | Re-checks installed and signed-in harnesses now. |
| `pinework device sync-config` | Pushes this machine's harness config to Pinework. |
| `pinework device restart` | Restarts the service. Live runs get up to 30 minutes to finish. |
| `pinework device install-service` | Starts the device manager at login. macOS only. |
| `pinework device uninstall-service` | Removes that login service. macOS only. |
| `pinework device logs` | Shows the device manager log, 200 lines by default. |
| `pinework device spool` | Lists deliveries the device gave up on. `--replay` requeues them. |
| `pinework device test-error` | Sends a test error to prove error reporting works. |
| `pinework folder add <name> <path>` | Registers a folder on this machine. `--device-id` picks another device. |
| `pinework folder list` | Lists your live folders and the repo each holds. |
| `pinework folder set-repo <fs_…> <repo-id>` | Sets the repo a folder holds. Pass `none` to clear it. |
| `pinework folder remove <fs_…>` | Removes a folder. |
| `pinework agent run --env cloud` | Runs an agent once in the place you name, `device` or `cloud`. |
| `pinework agent config set --environment device` | Sets an agent's environment, `device` or `cloud`. |

```bash theme={null}
pinework device status
pinework device set --max-concurrent 5
pinework folder add web-app /Users/sam/code/web-app
pinework agent run @deployer --env cloud --prompt "Run the release checklist"
```

## API

| Method | Path | Does |
| - | - | - |
| GET | `/api/v1/devices` | Lists your own devices in this workspace, live and offline. |
| PATCH | `/api/v1/devices/:id` | Updates one of your devices. `:id` is its `deviceId`. |
| DELETE | `/api/v1/devices/:id` | Removes one of your devices and revokes its token. |
| GET | `/api/v1/folders` | Lists your folders. Returns `{ "sources": [...] }`. |
| POST | `/api/v1/folders` | Registers a folder. Body: `displayName`, `rootPath`, `deviceId`. |
| PATCH | `/api/v1/folders/:shortId` | Sets or clears `repoId`. |
| DELETE | `/api/v1/folders/:shortId` | Removes a folder. Returns 204. |
| GET | `/api/v1/folders/:shortId/git-remote` | Reads the folder's git remote and matches it to a workspace repo. |

The PATCH body takes any of these keys, and no others:

* `displayName`: 1 to 100 characters.
* `hostname`: 1 to 255 characters.
* `maxConcurrentRuns`: 1 to 10.
* `isWorkspaceDefault`: `true` makes this the workspace default device. `false` does nothing.
* `remoteControlRevoked`: `true` revokes the device as a remote controller. `false` clears that.

Only a person can call these routes. An agent gets 403.

## Errors

| Code | HTTP | When | What to do |
| - | - | - | - |
| `remote_access_forbidden` | 403 | You set `isWorkspaceDefault` or `remoteControlRevoked` on a device you do not own. | Ask the device's owner. |
| `device_not_found` | 404 | A PATCH sets `isWorkspaceDefault` or `remoteControlRevoked`, and the device is not in this workspace. | List devices and use a `deviceId` from the list. |
| `resource_not_found` | 404 | The device to make default is not in this workspace. | Check the `deviceId`. |
| `invalid_id_format` | 400 | `:id` is not a UUID, such as a `dvc_…` id. | Pass `deviceId`, not `publicId`. |
| `http_exception` | 403 | An agent called a device or folder route. | A person must make the call. |
| `validation_error` | 400 | A PATCH value is out of range, a key is unknown, or `rootPath` is not absolute. | Fix the field named in `param`. |
| `repo_already_mapped` | 409 | Another live folder on that device holds this repo. | Clear the repo on the other folder first. |

Some device and folder routes still answer with a plain `{ "error": "<message>" }` body. Examples are "Device not found" with 404 and "Device not owned by the current user" with 403. Read the HTTP status first. For shared codes, see [API conventions](/reference/api#errors).

## Limits

* Concurrent runs per device: 1 to 10, default 3.
* Heartbeat lease: 90 seconds. A device with no heartbeat for longer is `disconnected`.
* Retirement: 7 days with no heartbeat.
* Device display name: 100 characters. Hostname: 255 characters.

<CardGroup cols={2}>
  <Card title="Cloud and your machine" href="/concepts/cloud-and-device">
    How Pinework picks where a run executes.
  </Card>

  <Card title="Run on your Mac" href="/guides/run-on-your-mac">
    Connect your Mac and give an agent a folder.
  </Card>

  <Card title="Run" href="/reference/run">
    The run object and its `environment` field.
  </Card>

  <Card title="Agent" href="/reference/agent">
    Set an agent's environment and default folder.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.