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

# Private networks and WireGuard

> Create organization networks and register WireGuard configs over HTTPS.

Private networks belong to your organization. A private cluster or machine on a network is reachable only through a WireGuard tunnel. Every organization has a **default network**; you can also create named networks with their own address range and gateway.

Each WireGuard config is yours: the API key's creator owns it, and list endpoints return only your own configs. Generate the key pair on the machine that will connect. Send Ornn the public key only. Ornn returns a config with a `<your-wireguard-private-key>` placeholder that you fill in locally.

## Connect to the default network

**CLI**

```bash theme={null}
umask 077
wg genkey > wg-private.key

ornn vpn-connections configure <reservation-id> \
  --name laptop --private-key ./wg-private.key --output ./ornn-wg0.conf

sudo wg-quick up ./ornn-wg0.conf
```

The CLI derives the public key locally and writes the completed config with owner-only permissions.

**REST**

```bash theme={null}
export ORNN_API_URL='https://api.ornn.com'
umask 077
wg genkey > wg-private.key
wg pubkey < wg-private.key > wg-public.key

jq -n --arg key "$(cat wg-public.key)" '{public_key: $key}' > peer-request.json
curl --fail-with-body -X POST "$ORNN_API_URL/v1/orchestrator/networks/shared/peers" \
  -H "Authorization: Bearer $ORNN_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @peer-request.json > peer.json

jq -er '.peer.status, .peer.assigned_ip' peer.json
jq -er '.config' peer.json \
  | sed "s|<your-wireguard-private-key>|$(cat wg-private.key)|" > ornn-wg0.conf
rm peer.json
sudo wg-quick up ./ornn-wg0.conf
```

**Python**

```python theme={null}
import os
import subprocess

import requests

API = "https://api.ornn.com"
headers = {"Authorization": f"Bearer {os.environ['ORNN_API_KEY']}"}

private_key = subprocess.run(
    ["wg", "genkey"], capture_output=True, text=True, check=True
).stdout.strip()
public_key = subprocess.run(
    ["wg", "pubkey"], input=private_key, capture_output=True, text=True, check=True
).stdout.strip()

response = requests.post(
    f"{API}/v1/orchestrator/networks/shared/peers",
    headers=headers,
    json={"public_key": public_key},
    timeout=30,
)
response.raise_for_status()
payload = response.json()
print(payload["peer"]["status"], payload["peer"]["assigned_ip"])

config = payload["config"].replace("<your-wireguard-private-key>", private_key)
fd = os.open("ornn-wg0.conf", os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
os.fchmod(fd, 0o600)  # also tightens a file that already existed
with os.fdopen(fd, "w") as handle:
    handle.write(config)
```

Expect HTTP `201`. Continue when `peer.status` is `active`. `pending_gateway` means the gateway has not applied the config yet; `failed` means it rejected or could not apply it. Repeating the request with the same public key returns the same config instead of creating another, so you can retry after a timeout. A `409` with `wireguard_public_key_already_used` means another user registered that key; generate a new pair.

Treat the completed config as a secret. To end access, [remove the config](/api-reference/networks/wireguard/default-delete); removing it does not delete the network.

## Create a dedicated network

Create a named network when you want a separate address range for a cluster. Creating a network starts its gateway in the background.

**CLI**

```bash theme={null}
ornn private-networks create --name research --cidr 10.20.0.0/16
ornn private-networks show research
```

**REST**

```bash theme={null}
curl --fail-with-body -X POST "$ORNN_API_URL/v1/orchestrator/networks" \
  -H "Authorization: Bearer $ORNN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name": "research", "cidr": "10.20.0.0/16"}' > network.json
export NETWORK_ID="$(jq -er '.network.id' network.json)"

status=""
for attempt in $(seq 1 60); do
  status="$(curl --fail-with-body --silent \
    "$ORNN_API_URL/v1/orchestrator/networks/$NETWORK_ID" \
    -H "Authorization: Bearer $ORNN_API_KEY" | jq -er '.status')"
  echo "network status: $status"
  case "$status" in
    active) break ;;
    degraded) echo "network provisioning failed" >&2; exit 1 ;;
  esac
  sleep 10
done
[ "$status" = active ] || { echo "network not active after 10 minutes" >&2; exit 1; }
```

**Python**

```python theme={null}
import os
import time

import requests

API = "https://api.ornn.com"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['ORNN_API_KEY']}"

created = session.post(
    f"{API}/v1/orchestrator/networks",
    json={"name": "research", "cidr": "10.20.0.0/16"},
    timeout=30,
)
created.raise_for_status()
network_id = created.json()["network"]["id"]

deadline = time.monotonic() + 600
while True:
    network = session.get(f"{API}/v1/orchestrator/networks/{network_id}", timeout=30)
    network.raise_for_status()
    status = network.json()["status"]
    if status == "active":
        break
    if status == "degraded":
        raise RuntimeError(f"network {network_id} failed to provision")
    if time.monotonic() > deadline:
        raise TimeoutError(f"network still {status}")
    time.sleep(10)
print(network_id, status)
```

Omit `cidr` to let Ornn choose a free `/16`. A custom range must be private IPv4, from `/16` to `/24`, and must not overlap another network. Network names are unique in your organization, ignoring case. Creation is not idempotent: after a timeout, [list networks](/api-reference/networks/list) by name before retrying.

Continue only when `status` is `active`. `degraded` means provisioning failed; the `compartments` array shows which step failed. Contact [Ornn support](mailto:support@ornn.com) with the network ID.

Use the network ID as `network_id` with `network_mode: "private"` when you [create a cluster](/api-reference/clusters/create). To reach it, [add a WireGuard config to that network](/api-reference/networks/wireguard/create) with the same request body as the default network, then fill in and activate the config the same way.

## Delete a network

Delete a network only after removing its WireGuard configs and tearing down the clusters that use it. `attached_reservation_count` counts every connected peer, including machines; it must be `0`. Otherwise deletion returns `409` with `network_in_use`.

```bash theme={null}
curl --fail-with-body -X DELETE "$ORNN_API_URL/v1/orchestrator/networks/$NETWORK_ID" \
  -H "Authorization: Bearer $ORNN_API_KEY"
```

A `204` starts gateway teardown; the network reports `deleting` until it is gone. Poll [Get a private network](/api-reference/networks/get) until it returns `404`. From the CLI, use `ornn private-networks delete <name-or-id>`.


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