Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -590,12 +590,26 @@ Create a connection entry. The body includes a `name` field plus all entry field
| `display_name` | string | All | Friendly display name (shown as banner) |
| `prompt_credentials` | boolean | All | Prompt user for credentials at connect time |
| `jump_hosts` | array | All | Multi-hop SSH tunnel chain (same format as session creation) |
| `external_id` | string | All | Stable id of the record the entry is synced from (the [NetBox webhook](netbox.md#create-webhook-device-createdupdated) sends `netbox:device:<id>`). What `PUT ...?move=true` matches on; never used to connect. Preserved on update when omitted. |

For `spice` and `proxmox` entries, the `spice_*` and `proxmox_*` fields listed under [`POST /api/sessions`](#post-apisessions) apply here too. The `proxmox_token_secret` is write-only: it is never returned by the read endpoints (a `has_proxmox_token_secret` boolean indicates whether one is stored), and it is preserved on update when omitted.

### `PUT /api/addressbook/folders/:scope/:folder/entries/:entry` (admin)

Update a connection entry. Uses read-modify-write: reads existing entry from Vault, merges incoming fields on top. Credentials (`password`, `private_key`) that are omitted from the request are preserved from the existing entry. Jump host credentials are merged per-hop by index.
Update a connection entry, or create it if there is none of that name in the folder. Uses read-modify-write: reads existing entry from Vault, merges incoming fields on top. Credentials (`password`, `private_key`, `container_password`, `proxmox_token_secret`) that are omitted from the request are preserved from the existing entry, and so is `external_id`. Jump host credentials are merged per-hop by index. If the existing entry cannot be read for any reason other than not existing, the response is `502` and nothing is written, so a request without secrets never replaces an entry that has them.

**`?move=true`** is for sync sources such as the [NetBox webhook](netbox.md#create-webhook-device-createdupdated), where the folder or the name is computed from the device and can change. It needs `external_id` in the body and matches the device on that, never on its name:

1. If `:folder/:entry` exists with the same `external_id`, or with none (an entry made by hand before syncing began), it is updated in place.
2. Otherwise every entry in the scope is read, and the one carrying that `external_id` is moved to `:folder/:entry` by the server-side move below, credentials and credential variables included, and the update is then applied on top. The response says where it came from:

```json
{ "ok": true, "moved_from": { "folder": "mrgelato", "name": "fw01" } }
```

3. If no entry carries it, the entry is created.

Entries in other folders that merely share the name are never touched. Responses: `400` without `external_id`; `409` if `:folder/:entry` belongs to a different `external_id`, or if the `external_id` is stored on more than one entry in the scope (nothing is changed either way); `502` with `"copied": true` if the move copied the entry but could not remove the original (it then exists in both folders, and the update is not applied). The scan in step 2 reads every entry in the scope, but it only happens when the entry is not already where the request says, that is on first sight of a device and when it moves.

### `DELETE /api/addressbook/folders/:scope/:folder/entries/:entry` (admin)

Expand Down
12 changes: 8 additions & 4 deletions docs/netbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ Use Event Rule **conditions** to sync only the devices you want. You can filter

2. **Create the Webhook**:
- Name: `rustguac-sync-create`
- URL: `https://console.example.com/api/addressbook/folders/shared/netbox-sync/entries`
- HTTP method: POST
- URL: `https://console.example.com/api/addressbook/folders/shared/netbox-sync/entries/{{ data.name | lower }}?move=true`
- HTTP method: PUT
- HTTP content type: `application/json`
- Additional headers:
```
Expand All @@ -150,17 +150,21 @@ Use Event Rule **conditions** to sync only the devices you want. You can filter
- Body template:
```json
{
"name": "{{ data.name | lower }}",
"type": "{{ data.custom_fields.remote_protocol | default('ssh') }}",
"hostname": "{{ data.primary_ip4.address.split('/')[0] }}",
"port": {{ data.custom_fields.remote_port | default(22) }},
"display_name": "{{ data.name }} ({{ data.site.name }})",
"prompt_credentials": true
"prompt_credentials": true,
"external_id": "netbox:device:{{ data.id }}"
}
```

**Important:** The entry field is `type`, not `session_type` (it matches the Vault storage format). The hostname uses `.split('/')[0]` to strip the CIDR prefix from NetBox IP addresses (e.g. `10.0.0.1/24` → `10.0.0.1`). Avoid `regex_replace` and `cut` filters — they are not available in NetBox's Jinja2 environment.

`PUT` creates the entry on first sight and updates it afterwards, keeping any password or key someone has since stored on it in Vault (NetBox carries no secrets, so the webhook never sends any). `external_id` is the device's NetBox id, prefixed so a device and a virtual machine with the same numeric id cannot collide; it is what the entry is matched on from then on, so renaming the device in NetBox is a rename here, not a second entry beside an orphan.

`?move=true` matters when the folder is computed from the device, for example one folder per tenant: `.../folders/shared/{{ data.tenant.slug }}/entries/{{ data.name | lower }}?move=true`. When a device changes tenant, the entry carrying its `external_id` is found in the old tenant's folder and moved to the new one by the same server-side move as the Connections page uses, so its password, key and credential variables come along; the update is then applied. Entries that merely share the device's name in other folders (two tenants can each have a `fw01`) are never touched. Full rules and responses are under [`PUT .../entries/:entry`](api.md#put-apiaddressbookfoldersscopefolderentriesentry-admin).

### Create webhook: device deleted

1. **Create an Event Rule**:
Expand Down
Loading
Loading