# Registering providers

`dreamlake providers` stores provider configuration in DreamLake and associates it with your host enrollment. Use CLI 0.14.0 or Python 0.11.0 with the deployed provider registration API.

Install the clients:

```bash
npm install -g @dreamlake/dreamlake-cli@0.14.0
pip install dreamlake==0.11.0
```

For a native CLI installation, follow the [CLI installation guide](https://cli.dreamlake.ai/installation/).

## Register a provider

Save this declaration as `provider.json`. Replace the cluster, partitions and Vault entry with your values. Omit `credentialRef` if no reference is needed; never put credential contents in the declaration.

```json
{
  "version": 1,
  "name": "research-slurm",
  "kind": "slurm-ssh",
  "config": {
    "cluster": "research",
    "partitions": ["cpu"],
    "credentialRef": {"namespace": "team", "entryName": "research-ssh"}
  }
}
```

**Python tab:** The same declaration can be registered with Python:

**CLI**

```bash
dreamlake providers register team --file provider.json \
  --request-id research-register-001 --json
dreamlake providers list team --json
dreamlake providers show team/research-slurm --json
```

**Python**

```python
import json
from dreamlake import DreamLakeClient

client = DreamLakeClient()
with open("provider.json") as file:
    declaration = json.load(file)
registration = client.providers.register(
    "team", declaration=declaration, request_id="research-register-001"
)
provider_id = registration["resource"]["id"]
```

Keep the returned `resource.id`, `resource.revision`, `operationId` and your request ID. Every write requires an explicit request ID. Repeating the same request returns its original receipt. Reusing the ID with a different payload returns a conflict.

## Associate your Slurm enrollment

Save an association as `association.json`, using your enrollment ID and the provider revision. Paths must be literal absolute paths. `submissionRoot` is the shared directory as seen by the submission host; `computeRoot` is that directory as seen by the compute node. Executable paths must be valid on compute nodes.

```json
{
  "providerRevision": 1,
  "enrollmentId": "0123456789abcdef01234567",
  "runner": {
    "kind": "slurm",
    "partition": "cpu",
    "staging": {
      "submissionRoot": "/export/work/alice",
      "computeRoot": "/work/alice"
    },
    "environment": {
      "uvExecutable": "/opt/tools/uv",
      "pythonExecutable": "/usr/bin/python3"
    }
  }
}
```

Replace `PROVIDER_ID` below with the registration's `resource.id`.

**CLI**

```bash
dreamlake providers associate team/PROVIDER_ID --file association.json \
  --request-id research-associate-001 --json
dreamlake providers status team/PROVIDER_ID --json
```

**Python**

```python
with open("association.json") as file:
    association_input = json.load(file)
association = client.providers.associate(
    "team", provider_id, association=association_input,
    request_id="research-associate-001",
)
status = client.providers.status("team", provider_id)
```

The enrollment must belong to you in the same namespace. The partition and optional account must be permitted by the declaration. Status reports `not_checked` until readiness checks exist; registration does not test SSH, paths or executables, provision machines, or submit jobs. Kubernetes declarations are accepted, but Kubernetes runner associations are not yet supported.

## Recover a missing response

After a timeout or lost connection, look up the receipt using the original request ID:

**CLI**

```bash
dreamlake providers operations find team --request-id research-register-001 --json
dreamlake providers operations show team/OPERATION_ID --json
```

**Python**

```python
receipt = client.providers.operations.find("team", "research-register-001")
receipt = client.providers.operations.get("team", receipt["operationId"])
```

The client does not retry writes automatically. If no receipt is found, retry the original command with exactly the same input and request ID. A receipt is a historical snapshot; use `status` for current state. Receipts are visible to their creating user.

## Retire configuration

Use the current revision of the resource being retired. To retire only an association, include its ID and revision:

**CLI**

```bash
dreamlake providers retire team/PROVIDER_ID --association ASSOCIATION_ID \
  --revision 1 --request-id research-association-retire-001 --json
dreamlake providers retire team/PROVIDER_ID \
  --revision 1 --request-id research-provider-retire-001 --json
```

**Python**

```python
client.providers.retire(
    "team", provider_id,
    association_id=association["resource"]["id"],
    revision=association["resource"]["revision"],
    request_id="research-association-retire-001",
)
client.providers.retire(
    "team", provider_id, revision=registration["resource"]["revision"],
    request_id="research-provider-retire-001",
)
```

Retirement changes metadata. It does not stop jobs, remove hosts or delete Vault entries. Retire associations separately when you want their records marked retired. Declarations and associations are immutable in this version; editing declarations and upgrading legacy provider records are not yet supported.

## Errors and API reference

CLI `--json` errors include the request ID and, for HTTP failures, status and a recognized error code. Python API and transport failures raise `ProviderError` subclasses with `request_id`; HTTP failures also carry `status` and `code`. `ProviderConflictError` identifies a conflicting request or revision, and `ProviderNotFound` identifies an unavailable resource or receipt.

See the [HTTP contract and error codes](https://github.com/dreamlake-ai/dreamlake-workspace/blob/main/dreamlake-server/src/providers/README.md) for request shapes and limits.
