From 277f4017abc4a4faf5f120819b8f86debb5aa510 Mon Sep 17 00:00:00 2001 From: "guthrie@vast.ai" Date: Mon, 22 Jun 2026 19:05:30 -0700 Subject: [PATCH 01/26] docs(agents): use marketplace install flow for agent plugins Replace source-install steps with marketplace commands for the Claude Code, Codex, and Cursor plugins. Co-Authored-By: Claude Opus 4.8 (1M context) --- guides/get-started/agents.mdx | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/guides/get-started/agents.mdx b/guides/get-started/agents.mdx index ac2de3ba..62c02f48 100644 --- a/guides/get-started/agents.mdx +++ b/guides/get-started/agents.mdx @@ -60,42 +60,35 @@ Invoke them as **`/vastai:`** in Claude Code and **`/prompts:vast- - Install from source: + Inside Claude Code, add the marketplace and install the plugin: - ```bash - git clone https://github.com/vast-ai/vast-claude-plugin.git - claude --plugin-dir ./vast-claude-plugin + ```text + /plugin marketplace add vast-ai/vast-claude-plugin + /plugin install vastai ``` - - Marketplace install (`/plugin install vastai@…`) is coming once the plugin - is published to a Claude Code marketplace. For now, load it from source with - `--plugin-dir`. - - Then run `/vastai:setup` once to store your API key, register your SSH public key, and verify the credential. Source: [`vast-ai/vast-claude-plugin`](https://github.com/vast-ai/vast-claude-plugin). - Install from source: + In your terminal, add the marketplace and open the plugin browser: ```bash - git clone https://github.com/vast-ai/vast-codex-plugin.git - cd vast-codex-plugin - ./install.sh + codex plugin marketplace add vast-ai/vast-codex-plugin + codex plugin # plugin browser → pick "Vast.ai" → Install ``` - This installs the skills into `~/.agents/skills/` and the prompts into `~/.codex/prompts/`. Restart your Codex session, then run `/prompts:vast-setup` once. Source: [`vast-ai/vast-codex-plugin`](https://github.com/vast-ai/vast-codex-plugin). + Restart your Codex session, then run `/prompts:vast-setup` once. Source: [`vast-ai/vast-codex-plugin`](https://github.com/vast-ai/vast-codex-plugin). No slash commands — Cursor is natural-language driven. The plugin adds both skills plus an auto-attach rule that points Cursor at the renter skill when you edit infrastructure files (`*.tf`, `*.yaml`, `infra/`, `deploy/`). - In Cursor 2.5+, run the install command in the editor: + Install it from the Cursor Dashboard → **Settings → Plugins → Team Marketplaces → Import**, then paste: ```text - /add-plugin vastai + https://github.com/vast-ai/vast-cursor-plugin ``` - Or clone and run `install.sh` to install ahead of marketplace acceptance. Then say *"Configure my Vast.ai CLI."* in Agent chat to walk through setup. Source: [`vast-ai/vast-cursor-plugin`](https://github.com/vast-ai/vast-cursor-plugin). + Then say *"Configure my Vast.ai CLI."* in Agent chat to walk through setup. Source: [`vast-ai/vast-cursor-plugin`](https://github.com/vast-ai/vast-cursor-plugin). From 5939944b12c9a9900a0afd456f50e8db350b618e Mon Sep 17 00:00:00 2001 From: Abiola Akinnubi Date: Tue, 30 Jun 2026 12:44:15 -0700 Subject: [PATCH 02/26] Adding New Notifications Documentation with Webhook workflow examples --- api-reference/introduction.mdx | 1 + api-reference/openapi.yaml | 618 +++++++++++++++++ api-reference/openapi/yaml/notifications.yaml | 642 ++++++++++++++++++ docs.json | 33 + .../notifications/google-chat-webhook.mdx | 312 +++++++++ examples/notifications/slack-webhook.mdx | 332 +++++++++ guides/reference/account-settings.mdx | 6 +- guides/reference/notification-webhooks.mdx | 242 +++++++ guides/reference/notifications.mdx | 143 ++++ images/console-notification-webhook.png | Bin 0 -> 124714 bytes images/console-notifications-settings.png | Bin 0 -> 158734 bytes 11 files changed, 2327 insertions(+), 2 deletions(-) create mode 100644 api-reference/openapi/yaml/notifications.yaml create mode 100644 examples/notifications/google-chat-webhook.mdx create mode 100644 examples/notifications/slack-webhook.mdx create mode 100644 guides/reference/notification-webhooks.mdx create mode 100644 guides/reference/notifications.mdx create mode 100644 images/console-notification-webhook.png create mode 100644 images/console-notifications-settings.png diff --git a/api-reference/introduction.mdx b/api-reference/introduction.mdx index 6df3586c..be6f70bc 100644 --- a/api-reference/introduction.mdx +++ b/api-reference/introduction.mdx @@ -23,6 +23,7 @@ New to the API? Start with the [API Hello World](/api-reference/hello-world), it | [Rate limits & errors](/api-reference/rate-limits-and-errors) | Per-endpoint limits, error codes, retry guidance | | [Creating instances](/api-reference/creating-instances-with-api) | Search-and-rent flow, configuration options | | [Templates](/api-reference/creating-and-using-templates-with-api) | Template fields, creation, instance launch | +| [Notifications](/api-reference/notifications/list-notification-types) | Notification preferences, in-app notifications, and webhooks | | Endpoints | Full OpenAPI reference for every endpoint | ## Base URL diff --git a/api-reference/openapi.yaml b/api-reference/openapi.yaml index 848f2008..79df980e 100644 --- a/api-reference/openapi.yaml +++ b/api-reference/openapi.yaml @@ -4993,6 +4993,481 @@ paths: example: API requests too frequent endpoint threshold=3.0 tags: - Machines + /api/v0/notification-types/: + get: + summary: list notification types + description: List the notification types available to the authenticated user, + including their display names, contexts, topics, and default channel settings. + security: + - BearerAuth: [] + tags: + - Notifications + responses: + '200': + description: Notification types returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notification_types: + type: array + items: + $ref: '#/components/schemas/NotificationType' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/users/{user_id}/notification-prefs/: + get: + summary: get notification preferences + description: Read notification preferences for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: user_id + in: path + required: true + schema: + type: integer + description: ID of the authenticated user. + responses: + '200': + description: Notification preferences returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notification_preferences: + $ref: '#/components/schemas/NotificationPreferences' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + summary: update notification preferences + description: Replace notification preferences for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: user_id + in: path + required: true + schema: + type: integer + description: ID of the authenticated user. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - notification_preferences + properties: + notification_preferences: + $ref: '#/components/schemas/NotificationPreferences' + example: + notification_preferences: + client: + low_credit: + email: true + webhooks: false + responses: + '200': + description: Notification preferences updated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notification_preferences: + $ref: '#/components/schemas/NotificationPreferences' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/notifications/inbox/: + get: + summary: get notification inbox + description: Read recent in-app notifications for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + responses: + '200': + description: Inbox returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notifications: + type: array + items: + $ref: '#/components/schemas/InAppNotification' + last_seen_at: + type: number + format: float + nullable: true + seen_through_at: + type: number + format: float + nullable: true + unread_count: + type: integer + example: 2 + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + summary: mark notification inbox as seen + description: Mark in-app notifications as seen using a timestamp returned by + the inbox endpoint. + security: + - BearerAuth: [] + tags: + - Notifications + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - seen_through_at + properties: + seen_through_at: + type: number + format: float + description: Timestamp returned by `GET /api/v0/notifications/inbox/`. + example: + seen_through_at: 1772490000.123 + responses: + '200': + description: Inbox marked seen successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + last_seen_at: + type: number + format: float + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/: + get: + summary: list notification webhooks + description: List notification webhooks for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + responses: + '200': + description: Webhooks returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhooks: + type: array + items: + $ref: '#/components/schemas/NotificationWebhook' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: create notification webhook + description: Create a notification webhook for one or more notification type + keys. The response includes the signing secret; store it immediately. + security: + - BearerAuth: [] + tags: + - Notifications + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookCreateRequest' + example: + name: Ops notifications + webhook_url: https://example.com/vast/webhooks + event_types: + - client:low_credit + - host:machine_offline + responses: + '200': + description: Webhook created successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhook: + $ref: '#/components/schemas/NotificationWebhookWithSecret' + enabled_notification_preferences: + type: array + items: + type: string + example: + - client:low_credit + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/{id}/: + put: + summary: update notification webhook + description: Update a notification webhook's name, URL, or event subscriptions. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookUpdateRequest' + example: + name: Primary ops notifications + event_types: + - client:low_credit + - client:outbid + responses: + '200': + description: Webhook updated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhook: + $ref: '#/components/schemas/NotificationWebhook' + enabled_notification_preferences: + type: array + items: + type: string + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + summary: delete notification webhook + description: Delete a notification webhook. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + responses: + '200': + description: Webhook deleted successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/{id}/rotate-secret/: + post: + summary: rotate notification webhook secret + description: Rotate the webhook signing secret. Store the returned secret immediately. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + responses: + '200': + description: Webhook secret rotated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhook: + $ref: '#/components/schemas/NotificationWebhookWithSecret' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/{id}/test/: + post: + summary: test notification webhook + description: Send a test delivery to the webhook URL using the same request + format and signature headers as normal webhook delivery. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + responses: + '200': + description: Test delivery accepted + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + msg: + type: string + example: Test webhook delivered + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /api/v0/instances/prepay/{id}/: put: summary: prepay instance @@ -8817,6 +9292,149 @@ components: success: type: boolean example: true + NotificationType: + type: object + properties: + key: + type: string + description: Full notification type key. + example: client:low_credit + slug: + type: string + example: low_credit + context: + type: string + enum: + - client + - host + topic: + type: string + nullable: true + example: billing + category: + type: string + example: billing + display_name: + type: string + example: Low balance notifications + default_preferences: + type: object + additionalProperties: + type: boolean + example: + email: true + webhooks: false + mandatory_email: + type: boolean + example: false + NotificationPreferences: + type: object + additionalProperties: + type: object + additionalProperties: + type: object + additionalProperties: + type: boolean + example: + client: + low_credit: + email: true + webhooks: false + InAppNotification: + type: object + properties: + event_id: + type: string + example: 7e9a2c4e6f9e4a24a53b77c2d8e3f0aa + notif_type: + type: string + example: low_credit + subject: + type: string + example: Warning - Your Vast.ai Credit Balance Is Getting Low + message: + type: string + example: Your Vast.ai balance is below your configured threshold. + timestamp: + type: number + format: float + example: 1772490000.123 + NotificationWebhook: + type: object + properties: + id: + type: integer + example: 42 + user_id: + type: integer + example: 123 + name: + type: string + nullable: true + example: Ops notifications + webhook_url: + type: string + format: uri + example: https://example.com/vast/webhooks + event_types: + type: array + items: + type: string + example: + - client:low_credit + - host:machine_offline + created_at: + type: number + format: float + example: 1772490000.0 + updated_at: + type: number + format: float + example: 1772490000.0 + NotificationWebhookWithSecret: + allOf: + - $ref: '#/components/schemas/NotificationWebhook' + - type: object + properties: + webhook_secret: + type: string + description: Secret used to verify `X-Vast-Signature-256`. + example: + NotificationWebhookCreateRequest: + type: object + required: + - webhook_url + - event_types + properties: + name: + type: string + nullable: true + maxLength: 120 + webhook_url: + type: string + format: uri + maxLength: 2048 + event_types: + type: array + minItems: 1 + items: + type: string + NotificationWebhookUpdateRequest: + type: object + properties: + name: + type: string + nullable: true + maxLength: 120 + webhook_url: + type: string + format: uri + maxLength: 2048 + event_types: + type: array + minItems: 1 + items: + type: string Template: type: object properties: diff --git a/api-reference/openapi/yaml/notifications.yaml b/api-reference/openapi/yaml/notifications.yaml new file mode 100644 index 00000000..2d332a46 --- /dev/null +++ b/api-reference/openapi/yaml/notifications.yaml @@ -0,0 +1,642 @@ +openapi: 3.0.0 +info: + title: Vast.ai Notifications API + description: Manage notification types, preferences, in-app notifications, and notification webhooks. + version: 1.0.0 +servers: +- url: https://console.vast.ai + description: Production server +paths: + /api/v0/notification-types/: + get: + summary: list notification types + description: | + List the notification types available to the authenticated user, including their display names, contexts, topics, and default channel settings. + security: + - BearerAuth: [] + tags: + - Notifications + responses: + '200': + description: Notification types returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notification_types: + type: array + items: + $ref: '#/components/schemas/NotificationType' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/users/{user_id}/notification-prefs/: + get: + summary: get notification preferences + description: Read notification preferences for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: user_id + in: path + required: true + schema: + type: integer + description: ID of the authenticated user. + responses: + '200': + description: Notification preferences returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notification_preferences: + $ref: '#/components/schemas/NotificationPreferences' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + summary: update notification preferences + description: Replace notification preferences for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: user_id + in: path + required: true + schema: + type: integer + description: ID of the authenticated user. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - notification_preferences + properties: + notification_preferences: + $ref: '#/components/schemas/NotificationPreferences' + example: + notification_preferences: + client: + low_credit: + email: true + webhooks: false + responses: + '200': + description: Notification preferences updated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notification_preferences: + $ref: '#/components/schemas/NotificationPreferences' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/notifications/inbox/: + get: + summary: get notification inbox + description: Read recent in-app notifications for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + responses: + '200': + description: Inbox returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + notifications: + type: array + items: + $ref: '#/components/schemas/InAppNotification' + last_seen_at: + type: number + format: float + nullable: true + seen_through_at: + type: number + format: float + nullable: true + unread_count: + type: integer + example: 2 + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + summary: mark notification inbox as seen + description: Mark in-app notifications as seen using a timestamp returned by the inbox endpoint. + security: + - BearerAuth: [] + tags: + - Notifications + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - seen_through_at + properties: + seen_through_at: + type: number + format: float + description: Timestamp returned by `GET /api/v0/notifications/inbox/`. + example: + seen_through_at: 1772490000.123 + responses: + '200': + description: Inbox marked seen successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + last_seen_at: + type: number + format: float + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/: + get: + summary: list notification webhooks + description: List notification webhooks for the authenticated user. + security: + - BearerAuth: [] + tags: + - Notifications + responses: + '200': + description: Webhooks returned successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhooks: + type: array + items: + $ref: '#/components/schemas/NotificationWebhook' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: create notification webhook + description: | + Create a notification webhook for one or more notification type keys. The response includes the signing secret; store it immediately. + security: + - BearerAuth: [] + tags: + - Notifications + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookCreateRequest' + example: + name: Ops notifications + webhook_url: https://example.com/vast/webhooks + event_types: + - client:low_credit + - host:machine_offline + responses: + '200': + description: Webhook created successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhook: + $ref: '#/components/schemas/NotificationWebhookWithSecret' + enabled_notification_preferences: + type: array + items: + type: string + example: + - client:low_credit + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/{id}/: + put: + summary: update notification webhook + description: Update a notification webhook's name, URL, or event subscriptions. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/NotificationWebhookUpdateRequest' + example: + name: Primary ops notifications + event_types: + - client:low_credit + - client:outbid + responses: + '200': + description: Webhook updated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhook: + $ref: '#/components/schemas/NotificationWebhook' + enabled_notification_preferences: + type: array + items: + type: string + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + summary: delete notification webhook + description: Delete a notification webhook. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + responses: + '200': + description: Webhook deleted successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/{id}/rotate-secret/: + post: + summary: rotate notification webhook secret + description: Rotate the webhook signing secret. Store the returned secret immediately. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + responses: + '200': + description: Webhook secret rotated successfully + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + webhook: + $ref: '#/components/schemas/NotificationWebhookWithSecret' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /api/v0/webhooks/{id}/test/: + post: + summary: test notification webhook + description: Send a test delivery to the webhook URL using the same request format and signature headers as normal webhook delivery. + security: + - BearerAuth: [] + tags: + - Notifications + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: Webhook ID. + responses: + '200': + description: Test delivery accepted + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + msg: + type: string + example: Test webhook delivered + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + description: API key must be provided in the Authorization header + schemas: + NotificationType: + type: object + properties: + key: + type: string + description: Full notification type key. + example: client:low_credit + slug: + type: string + example: low_credit + context: + type: string + enum: + - client + - host + topic: + type: string + nullable: true + example: billing + category: + type: string + example: billing + display_name: + type: string + example: Low balance notifications + default_preferences: + type: object + additionalProperties: + type: boolean + example: + email: true + webhooks: false + mandatory_email: + type: boolean + example: false + NotificationPreferences: + type: object + additionalProperties: + type: object + additionalProperties: + type: object + additionalProperties: + type: boolean + example: + client: + low_credit: + email: true + webhooks: false + InAppNotification: + type: object + properties: + event_id: + type: string + example: 7e9a2c4e6f9e4a24a53b77c2d8e3f0aa + notif_type: + type: string + example: low_credit + subject: + type: string + example: Warning - Your Vast.ai Credit Balance Is Getting Low + message: + type: string + example: Your Vast.ai balance is below your configured threshold. + timestamp: + type: number + format: float + example: 1772490000.123 + NotificationWebhook: + type: object + properties: + id: + type: integer + example: 42 + user_id: + type: integer + example: 123 + name: + type: string + nullable: true + example: Ops notifications + webhook_url: + type: string + format: uri + example: https://example.com/vast/webhooks + event_types: + type: array + items: + type: string + example: + - client:low_credit + - host:machine_offline + created_at: + type: number + format: float + example: 1772490000.0 + updated_at: + type: number + format: float + example: 1772490000.0 + NotificationWebhookWithSecret: + allOf: + - $ref: '#/components/schemas/NotificationWebhook' + - type: object + properties: + webhook_secret: + type: string + description: Secret used to verify `X-Vast-Signature-256`. + example: + NotificationWebhookCreateRequest: + type: object + required: + - webhook_url + - event_types + properties: + name: + type: string + nullable: true + maxLength: 120 + webhook_url: + type: string + format: uri + maxLength: 2048 + event_types: + type: array + minItems: 1 + items: + type: string + NotificationWebhookUpdateRequest: + type: object + properties: + name: + type: string + nullable: true + maxLength: 120 + webhook_url: + type: string + format: uri + maxLength: 2048 + event_types: + type: array + minItems: 1 + items: + type: string + Error: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + msg: + type: string diff --git a/docs.json b/docs.json index acf42bcf..0e8e75d0 100644 --- a/docs.json +++ b/docs.json @@ -172,6 +172,13 @@ "guides/pricing", "guides/reference/account-settings", "guides/reference/billing", + { + "group": "Notifications", + "pages": [ + "guides/reference/notifications", + "guides/reference/notification-webhooks" + ] + }, "guides/reference/keys", "guides/reference/two-factor-authentication", "guides/reference/referral-program", @@ -577,6 +584,24 @@ "api-reference/creating-and-using-templates-with-api" ] }, + { + "group": "Notifications", + "icon": "bell", + "openapi": "api-reference/openapi.yaml", + "pages": [ + "GET /api/v0/notification-types/", + "GET /api/v0/users/{user_id}/notification-prefs/", + "PUT /api/v0/users/{user_id}/notification-prefs/", + "GET /api/v0/notifications/inbox/", + "PUT /api/v0/notifications/inbox/", + "GET /api/v0/webhooks/", + "POST /api/v0/webhooks/", + "PUT /api/v0/webhooks/{id}/", + "DELETE /api/v0/webhooks/{id}/", + "POST /api/v0/webhooks/{id}/rotate-secret/", + "POST /api/v0/webhooks/{id}/test/" + ] + }, { "group": "Endpoints", "icon": "webhook", @@ -678,6 +703,14 @@ "examples/serving-infrastructure/sglang-router-vast" ] }, + { + "group": "Notifications", + "icon": "bell", + "pages": [ + "examples/notifications/slack-webhook", + "examples/notifications/google-chat-webhook" + ] + }, { "group": "Migration guides", "icon": "arrow-right-arrow-left", diff --git a/examples/notifications/google-chat-webhook.mdx b/examples/notifications/google-chat-webhook.mdx new file mode 100644 index 00000000..bf15f601 --- /dev/null +++ b/examples/notifications/google-chat-webhook.mdx @@ -0,0 +1,312 @@ +--- +title: "Send Notifications to Google Chat" +slug: "vast-notifications-google-chat-webhook" +createdAt: "Tue Jun 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" +updatedAt: "Tue Jun 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" +--- + +Use this example to send Vast.ai notification webhooks into a Google Chat space. Vast.ai sends a standard notification payload, and the adapter below verifies the Vast.ai signature before converting it into the `{"text": "..."}` format Google Chat expects. + +The flow is: + +```text +Vast.ai notification -> your HTTPS webhook URL -> local adapter -> Google Chat +``` + +## Prerequisites + +- A Vast.ai API key from [Keys](https://cloud.vast.ai/manage-keys/) +- A Google Chat space where you can add an incoming webhook +- Python 3.10 or newer +- A public HTTPS URL that forwards to your local machine for testing, such as Tunnelmole, ngrok, or Cloudflare Tunnel + +## Review Notification Settings + +Open [Account Settings](https://cloud.vast.ai/account/) and review **Notification and Webhook Settings**. The notification groups shown here are the same event groups you can subscribe to through the API. + + + ![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + + +## Create a Google Chat Incoming Webhook + +In Google Chat: + +1. Create or open a space. +2. Open the space menu. +3. Select **Apps & integrations**. +4. Add an incoming webhook. +5. Copy the Google Chat webhook URL. + +Keep that URL private. It lets anyone who has it post into the space. + +## Create the Adapter + +Create a working directory and install the only Python dependency: + +```bash +mkdir vast-google-chat-notifications +cd vast-google-chat-notifications +python3 -m venv .venv +. .venv/bin/activate +pip install requests +``` + +Create `vast_google_chat_adapter.py`: + +```python +#!/usr/bin/env python3 +import hashlib +import hmac +import json +import os +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +import requests + + +GOOGLE_CHAT_URL = os.environ["GOOGLE_CHAT_URL"] +VAST_WEBHOOK_SECRET = os.environ["VAST_WEBHOOK_SECRET"] +PORT = int(os.environ.get("PORT", "8787")) +MAX_SIGNATURE_AGE_SECONDS = 300 + + +def verify_vast_signature(headers, raw_body: bytes) -> bool: + timestamp = headers.get("X-Vast-Timestamp", "") + signature = headers.get("X-Vast-Signature-256", "") + + if not timestamp or not signature.startswith("sha256="): + return False + + try: + age = abs(time.time() - int(timestamp)) + except ValueError: + return False + + if age > MAX_SIGNATURE_AGE_SECONDS: + return False + + signed = timestamp.encode("utf-8") + b"." + raw_body + digest = hmac.new( + VAST_WEBHOOK_SECRET.encode("utf-8"), + signed, + hashlib.sha256, + ).hexdigest() + return hmac.compare_digest(signature, f"sha256={digest}") + + +def google_chat_text(payload: dict) -> str: + subject = payload.get("subject") or "Vast.ai notification" + message = payload.get("message") or json.dumps(payload, sort_keys=True) + notif_type = payload.get("notif_type") + event_id = payload.get("event_id") + + lines = [subject, "", str(message)] + details = [] + if notif_type: + details.append(f"type={notif_type}") + if event_id: + details.append(f"event_id={event_id}") + if details: + lines.extend(["", " ".join(details)]) + return "\n".join(lines) + + +class Handler(BaseHTTPRequestHandler): + def _json(self, status: int, body: dict): + data = json.dumps(body).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def do_GET(self): + if self.path == "/health": + self._json(200, {"ok": True}) + return + self._json(404, {"ok": False, "error": "not_found"}) + + def do_POST(self): + length = int(self.headers.get("Content-Length", "0") or "0") + raw_body = self.rfile.read(length) + + if not verify_vast_signature(self.headers, raw_body): + self._json(401, {"ok": False, "error": "invalid_signature"}) + return + + try: + payload = json.loads(raw_body.decode("utf-8") or "{}") + except json.JSONDecodeError: + self._json(400, {"ok": False, "error": "invalid_json"}) + return + + if not isinstance(payload, dict): + self._json(400, {"ok": False, "error": "invalid_payload"}) + return + + response = requests.post( + GOOGLE_CHAT_URL, + json={"text": google_chat_text(payload)}, + timeout=10, + ) + if response.status_code >= 400: + self._json( + 502, + {"ok": False, "google_chat_status": response.status_code}, + ) + return + + print( + f"forwarded notif_type={payload.get('notif_type')} " + f"event_id={payload.get('event_id')}", + flush=True, + ) + self._json(200, {"ok": True}) + + def log_message(self, fmt, *args): + print(f"{self.address_string()} - {fmt % args}", flush=True) + + +if __name__ == "__main__": + print(f"listening on http://127.0.0.1:{PORT}", flush=True) + ThreadingHTTPServer(("127.0.0.1", PORT), Handler).serve_forever() +``` + +## Expose the Adapter Over HTTPS + +Vast.ai requires an HTTPS webhook URL. This example uses `tmole`; if you use another tunnel, run the equivalent command and copy the HTTPS URL it prints. + +In one terminal, start a tunnel to the adapter port: + +```bash +tmole 8787 +``` + +Copy the HTTPS URL printed by the tunnel and export it in another terminal: + +```bash +read -rp "Public HTTPS webhook URL: " PUBLIC_WEBHOOK_URL +export PUBLIC_WEBHOOK_URL +``` + +## Create the Vast.ai Webhook + +Set your real Vast.ai API key and Google Chat webhook URL: + +```bash +read -rsp "Vast.ai API key: " VAST_API_KEY +export VAST_API_KEY +echo + +read -rsp "Google Chat webhook URL: " GOOGLE_CHAT_URL +export GOOGLE_CHAT_URL +echo +``` + +Create a Vast.ai webhook that sends common billing and instance events to your tunnel URL: + +```bash +cat > create-webhook.json < +Store `VAST_WEBHOOK_SECRET` securely. Vast.ai returns it when the webhook is created and when the secret is rotated. Normal list and update responses do not reveal it. + + +You can create the same webhook from the console, but the API flow is best for this example because the adapter needs the signing secret. + + + ![Create webhook modal with webhook name and webhook URL fields](/images/console-notification-webhook.png) + + +## Start the Adapter + +Start the adapter in the terminal where `GOOGLE_CHAT_URL` and `VAST_WEBHOOK_SECRET` are exported: + +```bash +python3 vast_google_chat_adapter.py +``` + +The adapter listens only on `127.0.0.1`. The HTTPS tunnel forwards public Vast.ai webhook deliveries to that local port. + +## Send a Test Delivery + +In another terminal, send Vast.ai's built-in test event: + +```bash +curl --fail-with-body -sS \ + -X POST "https://console.vast.ai/api/v0/webhooks/$WEBHOOK_ID/test/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +The adapter should print a forwarded event, and the Google Chat space should receive a Vast.ai webhook test message. + +## Trigger a Real Notification + +After the test event works, trigger one of the subscribed events: + +- Create an instance to receive `client:instance_created`. +- Start an instance to receive `client:instance_started`. +- Stop an instance to receive `client:instance_stopped`. +- Keep low-balance notifications enabled to receive `client:low_credit` when your configured threshold is reached. + +You can list available notification types at any time: + +```bash +curl -sS \ + -H "Authorization: Bearer $VAST_API_KEY" \ + "https://console.vast.ai/api/v0/notification-types/" +``` + +## Clean Up + +Delete the webhook when you are done testing: + +```bash +curl --fail-with-body -sS \ + -X DELETE "https://console.vast.ai/api/v0/webhooks/$WEBHOOK_ID/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +Then stop the adapter and the HTTPS tunnel. + +## Production Notes + +- Deploy the adapter on a stable HTTPS endpoint before using it for production alerts. +- Verify `X-Vast-Signature-256` before forwarding or acting on any event. +- Deduplicate deliveries by `event_id`; webhook delivery is at-least-once. +- Return `2xx` only after your adapter accepts the event. +- Do not register the Google Chat incoming webhook URL directly in Vast.ai. Google Chat expects its own message format, so it needs an adapter between Vast.ai and Google Chat. diff --git a/examples/notifications/slack-webhook.mdx b/examples/notifications/slack-webhook.mdx new file mode 100644 index 00000000..1ce10359 --- /dev/null +++ b/examples/notifications/slack-webhook.mdx @@ -0,0 +1,332 @@ +--- +title: "Send Notifications to Slack" +slug: "vast-notifications-slack-webhook" +createdAt: "Tue Jun 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" +updatedAt: "Tue Jun 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)" +--- + +Use this example to send Vast.ai notification webhooks into a Slack channel. Vast.ai sends a standard notification payload, and the adapter below verifies the Vast.ai signature before converting it into a Slack message. + +The flow is: + +```text +Vast.ai notification -> your HTTPS webhook URL -> local adapter -> Slack +``` + +## Prerequisites + +- A Vast.ai API key from [Keys](https://cloud.vast.ai/manage-keys/) +- A Slack workspace where you can create an incoming webhook +- Python 3.10 or newer +- A public HTTPS URL that forwards to your local machine for testing, such as Tunnelmole, ngrok, or Cloudflare Tunnel + +## Review Notification Settings + +Open [Account Settings](https://cloud.vast.ai/account/) and review **Notification and Webhook Settings**. The notification groups shown here are the same event groups you can subscribe to through the API. + + + ![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + + +## Create a Slack Incoming Webhook + +Create a Slack incoming webhook for the channel that should receive Vast.ai notifications. Slack's own setup guide is here: [Sending messages using incoming webhooks](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks). + +When Slack gives you the webhook URL, keep it private. Anyone who has that URL can post into the selected channel. + +## Create the Adapter + +Create a working directory and install the only Python dependency: + +```bash +mkdir vast-slack-notifications +cd vast-slack-notifications +python3 -m venv .venv +. .venv/bin/activate +pip install requests +``` + +Create `vast_slack_adapter.py`: + +```python +#!/usr/bin/env python3 +import hashlib +import hmac +import json +import os +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +import requests + + +SLACK_WEBHOOK_URL = os.environ["SLACK_WEBHOOK_URL"] +VAST_WEBHOOK_SECRET = os.environ["VAST_WEBHOOK_SECRET"] +PORT = int(os.environ.get("PORT", "8787")) +MAX_SIGNATURE_AGE_SECONDS = 300 + +CONSOLE = os.environ.get("VAST_CONSOLE", "https://cloud.vast.ai") +ACTION_URLS = { + "low_credit": f"{CONSOLE}/billing/", + "billing_failed": f"{CONSOLE}/billing/", + "payment_receipt": f"{CONSOLE}/billing/", + "instance_created": f"{CONSOLE}/instances/", + "instance_started": f"{CONSOLE}/instances/", + "instance_stopped": f"{CONSOLE}/instances/", + "instance_offline": f"{CONSOLE}/instances/", + "instance_online": f"{CONSOLE}/instances/", + "outbid": f"{CONSOLE}/instances/", + "upcoming_downtime": f"{CONSOLE}/instances/", + "machine_offline": f"{CONSOLE}/host/machines/", + "machine_error": f"{CONSOLE}/host/machines/", + "maintenance_scheduled": f"{CONSOLE}/host/machines/", + "webhook_test": CONSOLE, +} + + +def verify_vast_signature(headers, raw_body: bytes) -> bool: + timestamp = headers.get("X-Vast-Timestamp", "") + signature = headers.get("X-Vast-Signature-256", "") + + if not timestamp or not signature.startswith("sha256="): + return False + + try: + age = abs(time.time() - int(timestamp)) + except ValueError: + return False + + if age > MAX_SIGNATURE_AGE_SECONDS: + return False + + signed = timestamp.encode("utf-8") + b"." + raw_body + digest = hmac.new( + VAST_WEBHOOK_SECRET.encode("utf-8"), + signed, + hashlib.sha256, + ).hexdigest() + return hmac.compare_digest(signature, f"sha256={digest}") + + +def slack_message(payload: dict) -> dict: + subject = payload.get("subject") or "Vast.ai notification" + message = payload.get("message") or json.dumps(payload, sort_keys=True) + notif_type = payload.get("notif_type") or "notification" + event_id = payload.get("event_id") + action_url = ACTION_URLS.get(notif_type, CONSOLE) + + details = [f"type={notif_type}"] + if event_id: + details.append(f"event_id={event_id}") + + return { + "text": f"{subject}\n{message}\n{action_url}\n{' '.join(details)}" + } + + +class Handler(BaseHTTPRequestHandler): + def _json(self, status: int, body: dict): + data = json.dumps(body).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def do_GET(self): + if self.path == "/health": + self._json(200, {"ok": True}) + return + self._json(404, {"ok": False, "error": "not_found"}) + + def do_POST(self): + length = int(self.headers.get("Content-Length", "0") or "0") + raw_body = self.rfile.read(length) + + if not verify_vast_signature(self.headers, raw_body): + self._json(401, {"ok": False, "error": "invalid_signature"}) + return + + try: + payload = json.loads(raw_body.decode("utf-8") or "{}") + except json.JSONDecodeError: + self._json(400, {"ok": False, "error": "invalid_json"}) + return + + if not isinstance(payload, dict): + self._json(400, {"ok": False, "error": "invalid_payload"}) + return + + response = requests.post( + SLACK_WEBHOOK_URL, + json=slack_message(payload), + timeout=10, + ) + if response.status_code >= 400: + self._json(502, {"ok": False, "slack_status": response.status_code}) + return + + print( + f"forwarded notif_type={payload.get('notif_type')} " + f"event_id={payload.get('event_id')}", + flush=True, + ) + self._json(200, {"ok": True}) + + def log_message(self, fmt, *args): + print(f"{self.address_string()} - {fmt % args}", flush=True) + + +if __name__ == "__main__": + print(f"listening on http://127.0.0.1:{PORT}", flush=True) + ThreadingHTTPServer(("127.0.0.1", PORT), Handler).serve_forever() +``` + +## Expose the Adapter Over HTTPS + +Vast.ai requires an HTTPS webhook URL. This example uses `tmole`; if you use another tunnel, run the equivalent command and copy the HTTPS URL it prints. + +In one terminal, start a tunnel to the adapter port: + +```bash +tmole 8787 +``` + +Copy the HTTPS URL printed by the tunnel and export it in another terminal: + +```bash +read -rp "Public HTTPS webhook URL: " PUBLIC_WEBHOOK_URL +export PUBLIC_WEBHOOK_URL +``` + +## Create the Vast.ai Webhook + +Set your real Vast.ai API key and Slack webhook URL: + +```bash +read -rsp "Vast.ai API key: " VAST_API_KEY +export VAST_API_KEY +echo + +read -rsp "Slack incoming webhook URL: " SLACK_WEBHOOK_URL +export SLACK_WEBHOOK_URL +echo +``` + +Create a Vast.ai webhook that sends common billing and instance events to your tunnel URL: + +```bash +cat > create-webhook.json < +Store `VAST_WEBHOOK_SECRET` securely. Vast.ai returns it when the webhook is created and when the secret is rotated. Normal list and update responses do not reveal it. + + +You can create the same webhook from the console, but the API flow is best for this example because the adapter needs the signing secret. + + + ![Create webhook modal with webhook name and webhook URL fields](/images/console-notification-webhook.png) + + +## Start the Adapter + +Start the adapter in the terminal where `SLACK_WEBHOOK_URL` and `VAST_WEBHOOK_SECRET` are exported: + +```bash +python3 vast_slack_adapter.py +``` + +The adapter listens only on `127.0.0.1`. The HTTPS tunnel forwards public Vast.ai webhook deliveries to that local port. + +## Send a Test Delivery + +In another terminal, send Vast.ai's built-in test event: + +```bash +curl --fail-with-body -sS \ + -X POST "https://console.vast.ai/api/v0/webhooks/$WEBHOOK_ID/test/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +The adapter should print a forwarded event, and the Slack channel should receive a Vast.ai webhook test message. + +## Trigger a Real Notification + +After the test event works, trigger one of the subscribed events: + +- Create an instance to receive `client:instance_created`. +- Start an instance to receive `client:instance_started`. +- Stop an instance to receive `client:instance_stopped`. +- Keep low-balance notifications enabled to receive `client:low_credit` when your configured threshold is reached. + +You can list available notification types at any time: + +```bash +curl -sS \ + -H "Authorization: Bearer $VAST_API_KEY" \ + "https://console.vast.ai/api/v0/notification-types/" +``` + +## Clean Up + +Delete the webhook when you are done testing: + +```bash +curl --fail-with-body -sS \ + -X DELETE "https://console.vast.ai/api/v0/webhooks/$WEBHOOK_ID/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +Then stop the adapter and the HTTPS tunnel. + +## Workflow Builder Variant + +If you use a Slack Workflow Builder webhook trigger instead of a Slack incoming webhook, set the workflow's expected variables first. Then change the adapter's `slack_message` return value to match those variables, for example: + +```python +return { + "vast_notification_message": f"{subject}\n{message}", + "vast_action_url": action_url, +} +``` + +Do not register the Slack webhook URL directly in Vast.ai unless Slack is configured to accept the exact JSON payload Vast.ai sends. The adapter gives you control over message shape and signature verification. + +## Production Notes + +- Deploy the adapter on a stable HTTPS endpoint before using it for production alerts. +- Verify `X-Vast-Signature-256` before forwarding or acting on any event. +- Deduplicate deliveries by `event_id`; webhook delivery is at-least-once. +- Return `2xx` only after your adapter accepts the event. diff --git a/guides/reference/account-settings.mdx b/guides/reference/account-settings.mdx index ee469f94..8c55bc42 100644 --- a/guides/reference/account-settings.mdx +++ b/guides/reference/account-settings.mdx @@ -106,9 +106,11 @@ When you are finished editing your environment variables, make sure you select t ## Notification Settings -You can subscribe or unsubscribe from our email newsletter by selecting or unselecting this checkbox in the Notification Settings section. +Use Notification Settings to choose which account, billing, instance, and host events reach you by email, in the console, or through webhooks. See [Notifications](/guides/reference/notifications) for the full setup guide. -![](/images/console-setting-3.webp) + +![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + ## Cloud Connection diff --git a/guides/reference/notification-webhooks.mdx b/guides/reference/notification-webhooks.mdx new file mode 100644 index 00000000..a1c4e3ef --- /dev/null +++ b/guides/reference/notification-webhooks.mdx @@ -0,0 +1,242 @@ +--- +title: "Notification Webhooks" +sidebarTitle: "Webhooks" +description: "Send Vast.ai notification events to your own HTTPS endpoint for automation, monitoring, and audit workflows." +"canonical": "/guides/reference/notification-webhooks" +--- + +Notification webhooks deliver Vast.ai notification events to an HTTPS endpoint you operate. They are grouped under the notification system because they use the same notification types and preferences as email and in-app notifications. + +Use webhooks when a notification should become an action: page an operator when a hosted machine fails verification, update your dashboard when an instance starts, or pause automation when a low-balance event fires. + +## How Webhooks Fit Into Notifications + +A webhook subscribes to one or more notification type keys. When a matching event is produced and the webhook channel is enabled for that notification type, Vast.ai sends a signed `POST` request to your webhook URL. + +Creating or updating a webhook for an event also enables the webhook channel for that event in your notification preferences. + +## Create a Webhook in the Console + +1. Open [Account Settings](https://cloud.vast.ai/account/). +2. Go to **Notification Settings**. +3. Select the notification events you want to send to a webhook. +4. Click **Create webhook**. +5. Enter a webhook name and an HTTPS URL. +6. Click **Create**, then click **Save** on the notification settings form. + +Existing webhooks appear below the notification groups. You can edit the name or URL, delete the webhook, or remove a webhook badge from an individual notification event. + + + ![Create webhook modal with webhook name and webhook URL fields](/images/console-notification-webhook.png) + + + +Webhook changes are saved when you save the notification settings form. If you close the page before saving, the form changes are not persisted. + + +## Create a Webhook With the API + +List the available notification types first: + +```bash +curl -H "Authorization: Bearer $VAST_API_KEY" \ + "https://console.vast.ai/api/v0/notification-types/" +``` + +Create a webhook by passing an HTTPS URL and at least one event key: + +```bash +curl -X POST "https://console.vast.ai/api/v0/webhooks/" \ + -H "Authorization: Bearer $VAST_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Ops notifications", + "webhook_url": "https://example.com/vast/webhooks", + "event_types": ["client:low_credit", "host:machine_offline"] + }' +``` + +The response includes the webhook and its signing secret: + +```json +{ + "success": true, + "webhook": { + "id": 42, + "user_id": 123, + "name": "Ops notifications", + "webhook_url": "https://example.com/vast/webhooks", + "event_types": ["client:low_credit", "host:machine_offline"], + "webhook_secret": "", + "created_at": 1772490000.0, + "updated_at": 1772490000.0 + }, + "enabled_notification_preferences": [ + "client:low_credit", + "host:machine_offline" + ] +} +``` + + +Store `webhook_secret` when it is returned. It is revealed on creation and after secret rotation, not on normal list or update responses. + + +## Webhook Limits and Validation + +| Rule | Details | +| --- | --- | +| Maximum webhooks | 4 per user | +| URL scheme | `https://` only when creating or updating a webhook | +| URL length | 2048 characters maximum | +| Endpoint host | Must include a hostname | +| Delivery target | Must resolve to a public IP address; localhost and private network targets are rejected at delivery time | +| Redirects | Redirect responses are treated as permanent delivery failures | +| Name | Optional through the API, 120 characters maximum, no control characters | +| Events | `event_types` must contain at least one valid event key | + +Use full event keys such as `client:low_credit` and `host:machine_offline`. Full keys avoid ambiguity when a similar event exists for both renters and hosts. + +## Event Payload + +Vast.ai sends a JSON `POST` request: + +```json +{ + "event_id": "7e9a2c4e6f9e4a24a53b77c2d8e3f0aa", + "user_id": 123, + "notif_type": "low_credit", + "subject": "Warning - Your Vast.ai Credit Balance Is Getting Low", + "message": "Your Vast.ai balance is below your configured threshold.", + "timestamp": 1772490000.123 +} +``` + +Headers: + +| Header | Description | +| --- | --- | +| `Content-Type: application/json` | Payload format | +| `X-Vast-Event-Id` | Stable event ID. Use this to deduplicate retries. | +| `X-Vast-Delivery-Attempt` | 1 for the first attempt, then increments on retries. | +| `X-Vast-Timestamp` | Integer Unix timestamp used in the signature input. | +| `X-Vast-Signature-256` | `sha256=` plus the HMAC-SHA256 signature. | + + +The payload's `timestamp` remains a floating-point event timestamp. Signature verification uses the integer timestamp from `X-Vast-Timestamp`. + + +## Verify Signatures + +Verify every request before acting on it. Vast.ai signs the exact request body with your `webhook_secret`. + +The signature input is: + +```text +. +``` + +Python example: + +```python +import hmac +import hashlib +import time + + +def verify_vast_signature(headers, raw_body: bytes, webhook_secret: str) -> bool: + timestamp = headers.get("X-Vast-Timestamp", "") + signature = headers.get("X-Vast-Signature-256", "") + + if not timestamp or not signature.startswith("sha256="): + return False + + # Reject old requests to reduce replay risk. + if abs(time.time() - int(timestamp)) > 300: + return False + + signed = timestamp.encode("utf-8") + b"." + raw_body + expected = hmac.new( + webhook_secret.encode("utf-8"), + signed, + hashlib.sha256, + ).hexdigest() + + return hmac.compare_digest(signature, f"sha256={expected}") +``` + +Do not parse and re-serialize JSON before verification. Use the raw request body bytes exactly as received. + +## Retry Behavior + +Your endpoint should return a `2xx` status only after it has accepted the event. + +| Your response | Vast.ai behavior | +| --- | --- | +| `2xx` | Delivery is successful | +| `3xx` | Permanent failure; redirects are not followed | +| `400`-`499` except `408` and `429` | Permanent failure | +| `408`, `429`, or `5xx` | Retryable failure | +| Timeout or connection error | Retryable failure | + +Webhook delivery uses a 10 second request timeout. Design receivers to do minimal work in the request path: verify the signature, enqueue the event, return `2xx`, then process asynchronously. + + +Webhook delivery is at-least-once. Store `event_id` and ignore duplicates before triggering side effects. + + +## Manage Webhooks With the API + +List webhooks: + +```bash +curl -H "Authorization: Bearer $VAST_API_KEY" \ + "https://console.vast.ai/api/v0/webhooks/" +``` + +Update a webhook: + +```bash +curl -X PUT "https://console.vast.ai/api/v0/webhooks/42/" \ + -H "Authorization: Bearer $VAST_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Primary ops notifications", + "event_types": ["client:low_credit", "client:outbid"] + }' +``` + +Delete a webhook: + +```bash +curl -X DELETE "https://console.vast.ai/api/v0/webhooks/42/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +Rotate the signing secret: + +```bash +curl -X POST "https://console.vast.ai/api/v0/webhooks/42/rotate-secret/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +Test a webhook: + +```bash +curl -X POST "https://console.vast.ai/api/v0/webhooks/42/test/" \ + -H "Authorization: Bearer $VAST_API_KEY" +``` + +The test endpoint sends a `webhook_test` event with the same request format and signature headers as a real delivery. It does not create an inbox notification and is not retried. + +## Recommended Receiver Pattern + +1. Require `POST`. +2. Read the raw request body. +3. Verify `X-Vast-Signature-256`. +4. Reject stale timestamps. +5. Deduplicate by `event_id`. +6. Enqueue the event in your own system. +7. Return `204` or another `2xx` response quickly. + +This keeps the Vast.ai delivery path fast and gives your system control over downstream retries, paging, and processing. diff --git a/guides/reference/notifications.mdx b/guides/reference/notifications.mdx new file mode 100644 index 00000000..ca70b667 --- /dev/null +++ b/guides/reference/notifications.mdx @@ -0,0 +1,143 @@ +--- +title: "Notifications" +sidebarTitle: "Notifications" +description: "Choose which Vast.ai account, billing, instance, and host events reach you by email, in the console, or through webhooks." +"canonical": "/guides/reference/notifications" +--- + +Notifications let you decide which Vast.ai events should interrupt you, and where they should go. A renter may only need low-balance and instance lifecycle alerts in the console. A host may want machine health alerts by email. An automated platform can subscribe to the same events through webhooks and route them into its own incident, billing, or orchestration system. + +The notification system is shared across the web console and the API. The console gives users a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, inbox, and webhook tools. + +## Where to Find Notification Settings + +Open [Account Settings](https://cloud.vast.ai/account/) and go to **Notification Settings**. + + + ![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + + +The page groups events by the part of Vast.ai they affect: + +| Group | What it covers | +| --- | --- | +| **Account** | Email verification, password resets, email changes, team invitations, and similar account events | +| **Billing** | Low balance, payment receipts, billing failures, and other payment events | +| **Instance** | Instance creation, startup, stops, resumes, outbid events, offline/online state, contract end dates, downtime, and disk warnings | +| **Host** | Hosted machine health, verification failures, renter reports, maintenance, host disk warnings, and host-side contract events | + +Some events are only shown when they apply to your account. For example, host events are only relevant if you provide machines on Vast.ai. + +## How Delivery Works + +Each notification type has channel preferences. Today, the main public channels are: + +| Channel | Use it for | +| --- | --- | +| **Email** | Human-readable messages for events you want in your inbox | +| **In-app** | Console notification inbox entries for supported events | +| **Webhooks** | Machine-readable HTTP events for your own systems | + +Some emails are mandatory because they are tied to account access, billing safety, or imminent service impact. Mandatory email notifications cannot be fully disabled from the settings page. + +## Update Notifications in the Console + +1. Open **Account Settings**. +2. Go to **Notification Settings**. +3. Review each Account, Billing, Instance, and Host section. +4. Remove optional email badges for events you do not want by email. +5. For low-balance notifications, set the credit threshold that should trigger the warning. +6. Select events that should be sent to a webhook, then create or edit the webhook destination. +7. Click **Save**. + + +Set your low-balance threshold below your autobilling threshold. That gives you an early warning if an automatic charge fails before your balance reaches zero. + + +## Webhooks + +Webhooks are part of the notification system. They use the same event catalog and preferences as email and in-app notifications, but deliver events to an HTTPS endpoint that you operate. + +Use webhooks when you want to: + +- Trigger cleanup or orchestration when an instance changes state. +- Route host machine alerts into your monitoring stack. +- Mirror billing or account events into your own tools. +- Build an audit trail outside the Vast console. + +See [Notification Webhooks](/guides/reference/notification-webhooks) for setup, payloads, signing, retries, and API examples. + +## Programmatic Access + +Use these endpoints when building your own notification settings UI or automation: + +| Endpoint | Purpose | +| --- | --- | +| `GET /api/v0/notification-types/` | Discover available notification types, display names, and default channel settings | +| `GET /api/v0/users/{user_id}/notification-prefs/` | Read the authenticated user's notification preferences | +| `PUT /api/v0/users/{user_id}/notification-prefs/` | Replace notification preferences | +| `GET /api/v0/notifications/inbox/` | Read the current in-app notification inbox | +| `PUT /api/v0/notifications/inbox/` | Mark in-app notifications as seen using a timestamp returned by the API | +| `GET /api/v0/webhooks/` | List notification webhooks | +| `POST /api/v0/webhooks/` | Create a notification webhook | + +All of these endpoints require an API key: + +```bash +curl -H "Authorization: Bearer $VAST_API_KEY" \ + "https://console.vast.ai/api/v0/notification-types/" +``` + +## Notification Type Keys + +Notification types are identified by a `key`, such as `client:low_credit` or `host:machine_offline`. + +Use the full `key` when subscribing webhooks or updating preferences. Some events can exist in both renter and host contexts, so the full key avoids ambiguity. + +```json +{ + "key": "client:low_credit", + "slug": "low_credit", + "context": "client", + "topic": "billing", + "display_name": "Low balance notifications", + "default_preferences": { + "email": true, + "webhooks": false + }, + "mandatory_email": false +} +``` + +## In-App Inbox + +The console can show an in-app notification inbox for supported events. The inbox is a capped, expiring list of recent notifications. It is useful for status and workflow events, but email and webhooks are the durable channels for messages you need to retain outside the console. + +Read the inbox: + +```bash +curl -H "Authorization: Bearer $VAST_API_KEY" \ + "https://console.vast.ai/api/v0/notifications/inbox/" +``` + +The response includes `seen_through_at`. Send that exact value back when marking the batch as seen: + +```bash +curl -X PUT "https://console.vast.ai/api/v0/notifications/inbox/" \ + -H "Authorization: Bearer $VAST_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"seen_through_at": 1772490000.123}' +``` + +## Good Defaults + +Start with this setup: + +| If you are... | Recommended setup | +| --- | --- | +| Renting GPU instances manually | Keep low balance, outbid, downtime, and contract end date emails enabled | +| Running automated workloads | Add a webhook for instance lifecycle, downtime, outbid, and billing events | +| Hosting machines | Keep machine error, verification, report, maintenance, and low-disk alerts enabled | +| Building your own platform on Vast.ai | Use notification type keys, verify webhook signatures, and deduplicate by `event_id` | + +Review the settings periodically, especially after adding hosted machines, enabling autobilling, or connecting a new automation workflow. diff --git a/images/console-notification-webhook.png b/images/console-notification-webhook.png new file mode 100644 index 0000000000000000000000000000000000000000..acede02efc3d5cc4f46567c48572d7ae1e225d36 GIT binary patch literal 124714 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 z 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 akRG1@!2Oi^9Uu5_#oMd@eEzMCyZ;yB8umc| literal 0 HcmV?d00001 From 63f6b3b841c1bccc8b48c515154653cf7257f4b9 Mon Sep 17 00:00:00 2001 From: "guthrie@vast.ai" Date: Tue, 30 Jun 2026 18:10:15 -0700 Subject: [PATCH 03/26] docs(CLN-3495): segment renter/host notifications and de-duplicate API content - Remove host content from the renter notifications guide and account settings - Add host/notifications page for host machine, verification, and maintenance events - Add shared notification-channels snippet used by both renter and host pages - Reference the Notifications API from the webhooks guide instead of inlining curl/JSON - Drop the duplicate top-level Notifications API nav group (now only under Endpoints) - Harmonize signature-verification code and fix wording/naming consistency Co-Authored-By: Claude Opus 4.8 (1M context) --- docs.json | 19 +-- .../notifications/google-chat-webhook.mdx | 6 +- examples/notifications/slack-webhook.mdx | 11 +- guides/reference/account-settings.mdx | 4 +- guides/reference/notification-webhooks.mdx | 123 +++++------------- guides/reference/notifications.mdx | 99 ++++---------- host/notifications.mdx | 63 +++++++++ snippets/notifications/channels.mdx | 15 +++ 8 files changed, 142 insertions(+), 198 deletions(-) create mode 100644 host/notifications.mdx create mode 100644 snippets/notifications/channels.mdx diff --git a/docs.json b/docs.json index 0e8e75d0..e05bc851 100644 --- a/docs.json +++ b/docs.json @@ -584,24 +584,6 @@ "api-reference/creating-and-using-templates-with-api" ] }, - { - "group": "Notifications", - "icon": "bell", - "openapi": "api-reference/openapi.yaml", - "pages": [ - "GET /api/v0/notification-types/", - "GET /api/v0/users/{user_id}/notification-prefs/", - "PUT /api/v0/users/{user_id}/notification-prefs/", - "GET /api/v0/notifications/inbox/", - "PUT /api/v0/notifications/inbox/", - "GET /api/v0/webhooks/", - "POST /api/v0/webhooks/", - "PUT /api/v0/webhooks/{id}/", - "DELETE /api/v0/webhooks/{id}/", - "POST /api/v0/webhooks/{id}/rotate-secret/", - "POST /api/v0/webhooks/{id}/test/" - ] - }, { "group": "Endpoints", "icon": "webhook", @@ -628,6 +610,7 @@ "host/optimization-guide", "host/verification-stages", "host/how-to-self-test", + "host/notifications", "host/market-metrics", "host/datacenter-status", "host/payment", diff --git a/examples/notifications/google-chat-webhook.mdx b/examples/notifications/google-chat-webhook.mdx index bf15f601..209f2c5f 100644 --- a/examples/notifications/google-chat-webhook.mdx +++ b/examples/notifications/google-chat-webhook.mdx @@ -22,10 +22,10 @@ Vast.ai notification -> your HTTPS webhook URL -> local adapter -> Google Chat ## Review Notification Settings -Open [Account Settings](https://cloud.vast.ai/account/) and review **Notification and Webhook Settings**. The notification groups shown here are the same event groups you can subscribe to through the API. +Open [Account Settings](https://cloud.vast.ai/account/) and review **Notification Settings**. The notification groups shown here are the same event groups you can subscribe to through the API. - - ![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + + ![Notification Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) ## Create a Google Chat Incoming Webhook diff --git a/examples/notifications/slack-webhook.mdx b/examples/notifications/slack-webhook.mdx index 1ce10359..500c2bc8 100644 --- a/examples/notifications/slack-webhook.mdx +++ b/examples/notifications/slack-webhook.mdx @@ -22,10 +22,10 @@ Vast.ai notification -> your HTTPS webhook URL -> local adapter -> Slack ## Review Notification Settings -Open [Account Settings](https://cloud.vast.ai/account/) and review **Notification and Webhook Settings**. The notification groups shown here are the same event groups you can subscribe to through the API. +Open [Account Settings](https://cloud.vast.ai/account/) and review **Notification Settings**. The notification groups shown here are the same event groups you can subscribe to through the API. - - ![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + + ![Notification Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) ## Create a Slack Incoming Webhook @@ -77,9 +77,6 @@ ACTION_URLS = { "instance_online": f"{CONSOLE}/instances/", "outbid": f"{CONSOLE}/instances/", "upcoming_downtime": f"{CONSOLE}/instances/", - "machine_offline": f"{CONSOLE}/host/machines/", - "machine_error": f"{CONSOLE}/host/machines/", - "maintenance_scheduled": f"{CONSOLE}/host/machines/", "webhook_test": CONSOLE, } @@ -322,7 +319,7 @@ return { } ``` -Do not register the Slack webhook URL directly in Vast.ai unless Slack is configured to accept the exact JSON payload Vast.ai sends. The adapter gives you control over message shape and signature verification. +Do not point a Vast.ai webhook straight at the Slack URL — Slack expects its own message format, so the adapter must sit in between. The adapter also gives you control over message shape and signature verification. ## Production Notes diff --git a/guides/reference/account-settings.mdx b/guides/reference/account-settings.mdx index 8c55bc42..4190ee9f 100644 --- a/guides/reference/account-settings.mdx +++ b/guides/reference/account-settings.mdx @@ -106,10 +106,10 @@ When you are finished editing your environment variables, make sure you select t ## Notification Settings -Use Notification Settings to choose which account, billing, instance, and host events reach you by email, in the console, or through webhooks. See [Notifications](/guides/reference/notifications) for the full setup guide. +Use Notification Settings to choose which account, billing, and instance events reach you by email, in the console, or through webhooks. See [Notifications](/guides/reference/notifications) for the full setup guide. -![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + ![Notification Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) ## Cloud Connection diff --git a/guides/reference/notification-webhooks.mdx b/guides/reference/notification-webhooks.mdx index a1c4e3ef..5c78c6aa 100644 --- a/guides/reference/notification-webhooks.mdx +++ b/guides/reference/notification-webhooks.mdx @@ -7,13 +7,13 @@ description: "Send Vast.ai notification events to your own HTTPS endpoint for au Notification webhooks deliver Vast.ai notification events to an HTTPS endpoint you operate. They are grouped under the notification system because they use the same notification types and preferences as email and in-app notifications. -Use webhooks when a notification should become an action: page an operator when a hosted machine fails verification, update your dashboard when an instance starts, or pause automation when a low-balance event fires. +Use webhooks when a notification should become an action: update your dashboard when an instance starts, pause automation when a low-balance event fires, or trigger cleanup when an instance is outbid. The same mechanics apply to host events — see [Host Notifications](/host/notifications). ## How Webhooks Fit Into Notifications A webhook subscribes to one or more notification type keys. When a matching event is produced and the webhook channel is enabled for that notification type, Vast.ai sends a signed `POST` request to your webhook URL. -Creating or updating a webhook for an event also enables the webhook channel for that event in your notification preferences. +Subscribing a webhook to an event automatically turns on the webhook channel for that event in your notification preferences. ## Create a Webhook in the Console @@ -24,7 +24,7 @@ Creating or updating a webhook for an event also enables the webhook channel for 5. Enter a webhook name and an HTTPS URL. 6. Click **Create**, then click **Save** on the notification settings form. -Existing webhooks appear below the notification groups. You can edit the name or URL, delete the webhook, or remove a webhook badge from an individual notification event. +Existing webhooks appear below the notification groups. You can edit the name or URL, delete the webhook, or unsubscribe the webhook from an individual event. ![Create webhook modal with webhook name and webhook URL fields](/images/console-notification-webhook.png) @@ -34,54 +34,28 @@ Existing webhooks appear below the notification groups. You can edit the name or Webhook changes are saved when you save the notification settings form. If you close the page before saving, the form changes are not persisted. -## Create a Webhook With the API - -List the available notification types first: - -```bash -curl -H "Authorization: Bearer $VAST_API_KEY" \ - "https://console.vast.ai/api/v0/notification-types/" -``` - -Create a webhook by passing an HTTPS URL and at least one event key: - -```bash -curl -X POST "https://console.vast.ai/api/v0/webhooks/" \ - -H "Authorization: Bearer $VAST_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Ops notifications", - "webhook_url": "https://example.com/vast/webhooks", - "event_types": ["client:low_credit", "host:machine_offline"] - }' -``` +## Manage Webhooks With the API -The response includes the webhook and its signing secret: +This guide covers the delivery behavior you need to integrate safely — signing, retries, limits, and the receiver pattern. The API calls themselves (request bodies, response schemas, status codes) are documented in the API reference: -```json -{ - "success": true, - "webhook": { - "id": 42, - "user_id": 123, - "name": "Ops notifications", - "webhook_url": "https://example.com/vast/webhooks", - "event_types": ["client:low_credit", "host:machine_offline"], - "webhook_secret": "", - "created_at": 1772490000.0, - "updated_at": 1772490000.0 - }, - "enabled_notification_preferences": [ - "client:low_credit", - "host:machine_offline" - ] -} -``` +| Task | API reference | +| --- | --- | +| Discover subscribable event keys | [List notification types](/api-reference/notifications/list-notification-types) | +| Create a webhook | [Create notification webhook](/api-reference/notifications/create-notification-webhook) | +| List your webhooks | [List notification webhooks](/api-reference/notifications/list-notification-webhooks) | +| Update a webhook | [Update notification webhook](/api-reference/notifications/update-notification-webhook) | +| Delete a webhook | [Delete notification webhook](/api-reference/notifications/delete-notification-webhook) | +| Rotate the signing secret | [Rotate notification webhook secret](/api-reference/notifications/rotate-notification-webhook-secret) | +| Send a test delivery | [Test notification webhook](/api-reference/notifications/test-notification-webhook) | -Store `webhook_secret` when it is returned. It is revealed on creation and after secret rotation, not on normal list or update responses. +The signing secret is returned only when a webhook is created and when the secret is rotated — not on list or update responses. Store it immediately. + +The test endpoint sends a `webhook_test` event with the same request format and signature headers as a real delivery. It does not create an inbox notification and is not retried. + + ## Webhook Limits and Validation | Rule | Details | @@ -95,7 +69,7 @@ Store `webhook_secret` when it is returned. It is revealed on creation and after | Name | Optional through the API, 120 characters maximum, no control characters | | Events | `event_types` must contain at least one valid event key | -Use full event keys such as `client:low_credit` and `host:machine_offline`. Full keys avoid ambiguity when a similar event exists for both renters and hosts. +Use full notification type keys such as `client:low_credit` and `client:outbid`. Because a similar event can exist for both renters and hosts, the full key — including its `client:` or `host:` prefix — avoids ambiguity. Host webhooks use `host:` keys, documented in [Host Notifications](/host/notifications). ## Event Payload @@ -112,6 +86,10 @@ Vast.ai sends a JSON `POST` request: } ``` + +The payload's `notif_type` is the short slug (for example `low_credit`), without the `client:` or `host:` context prefix used in subscription keys. If you subscribe one webhook to both a `client:` and a `host:` variant of the same event, use a dedicated webhook per context to tell them apart reliably. + + Headers: | Header | Description | @@ -152,7 +130,12 @@ def verify_vast_signature(headers, raw_body: bytes, webhook_secret: str) -> bool return False # Reject old requests to reduce replay risk. - if abs(time.time() - int(timestamp)) > 300: + try: + age = abs(time.time() - int(timestamp)) + except ValueError: + return False + + if age > 300: return False signed = timestamp.encode("utf-8") + b"." + raw_body @@ -169,7 +152,7 @@ Do not parse and re-serialize JSON before verification. Use the raw request body ## Retry Behavior -Your endpoint should return a `2xx` status only after it has accepted the event. +Return a `2xx` status only once you have safely accepted the event — that is, verified the signature and enqueued it. | Your response | Vast.ai behavior | | --- | --- | @@ -185,50 +168,6 @@ Webhook delivery uses a 10 second request timeout. Design receivers to do minima Webhook delivery is at-least-once. Store `event_id` and ignore duplicates before triggering side effects. -## Manage Webhooks With the API - -List webhooks: - -```bash -curl -H "Authorization: Bearer $VAST_API_KEY" \ - "https://console.vast.ai/api/v0/webhooks/" -``` - -Update a webhook: - -```bash -curl -X PUT "https://console.vast.ai/api/v0/webhooks/42/" \ - -H "Authorization: Bearer $VAST_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Primary ops notifications", - "event_types": ["client:low_credit", "client:outbid"] - }' -``` - -Delete a webhook: - -```bash -curl -X DELETE "https://console.vast.ai/api/v0/webhooks/42/" \ - -H "Authorization: Bearer $VAST_API_KEY" -``` - -Rotate the signing secret: - -```bash -curl -X POST "https://console.vast.ai/api/v0/webhooks/42/rotate-secret/" \ - -H "Authorization: Bearer $VAST_API_KEY" -``` - -Test a webhook: - -```bash -curl -X POST "https://console.vast.ai/api/v0/webhooks/42/test/" \ - -H "Authorization: Bearer $VAST_API_KEY" -``` - -The test endpoint sends a `webhook_test` event with the same request format and signature headers as a real delivery. It does not create an inbox notification and is not retried. - ## Recommended Receiver Pattern 1. Require `POST`. diff --git a/guides/reference/notifications.mdx b/guides/reference/notifications.mdx index ca70b667..0acc7b8c 100644 --- a/guides/reference/notifications.mdx +++ b/guides/reference/notifications.mdx @@ -1,20 +1,26 @@ --- title: "Notifications" sidebarTitle: "Notifications" -description: "Choose which Vast.ai account, billing, instance, and host events reach you by email, in the console, or through webhooks." +description: "Choose which Vast.ai account, billing, and instance events reach you by email, in the console, or through webhooks." "canonical": "/guides/reference/notifications" --- -Notifications let you decide which Vast.ai events should interrupt you, and where they should go. A renter may only need low-balance and instance lifecycle alerts in the console. A host may want machine health alerts by email. An automated platform can subscribe to the same events through webhooks and route them into its own incident, billing, or orchestration system. +import NotificationChannels from '/snippets/notifications/channels.mdx'; -The notification system is shared across the web console and the API. The console gives users a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, inbox, and webhook tools. +Notifications let you decide which Vast.ai events you want to be told about, and where those messages go. A renter may only need low-balance and instance lifecycle alerts in the console. An automated platform can subscribe to the same events through webhooks and route them into its own incident, billing, or orchestration system. + +The notification system is shared across the web console and the API. The console gives you a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, inbox, and webhook tools. + + +This page covers renter notifications. If you also provide machines on Vast.ai, see [Host Notifications](/host/notifications) for host machine, verification, and maintenance events. + ## Where to Find Notification Settings Open [Account Settings](https://cloud.vast.ai/account/) and go to **Notification Settings**. - - ![Notification and Webhook Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) + + ![Notification Settings page with Account, Billing, and Instance notification groups](/images/console-notifications-settings.png) The page groups events by the part of Vast.ai they affect: @@ -24,28 +30,17 @@ The page groups events by the part of Vast.ai they affect: | **Account** | Email verification, password resets, email changes, team invitations, and similar account events | | **Billing** | Low balance, payment receipts, billing failures, and other payment events | | **Instance** | Instance creation, startup, stops, resumes, outbid events, offline/online state, contract end dates, downtime, and disk warnings | -| **Host** | Hosted machine health, verification failures, renter reports, maintenance, host disk warnings, and host-side contract events | - -Some events are only shown when they apply to your account. For example, host events are only relevant if you provide machines on Vast.ai. - -## How Delivery Works -Each notification type has channel preferences. Today, the main public channels are: +Some events are only shown when they apply to your account. For example, team-invitation events only appear if you belong to a team. -| Channel | Use it for | -| --- | --- | -| **Email** | Human-readable messages for events you want in your inbox | -| **In-app** | Console notification inbox entries for supported events | -| **Webhooks** | Machine-readable HTTP events for your own systems | - -Some emails are mandatory because they are tied to account access, billing safety, or imminent service impact. Mandatory email notifications cannot be fully disabled from the settings page. + ## Update Notifications in the Console 1. Open **Account Settings**. 2. Go to **Notification Settings**. -3. Review each Account, Billing, Instance, and Host section. -4. Remove optional email badges for events you do not want by email. +3. Review each Account, Billing, and Instance section. +4. Turn off email for any optional event you do not want in your inbox. 5. For low-balance notifications, set the credit threshold that should trigger the warning. 6. Select events that should be sent to a webhook, then create or edit the webhook destination. 7. Click **Save**. @@ -61,73 +56,26 @@ Webhooks are part of the notification system. They use the same event catalog an Use webhooks when you want to: - Trigger cleanup or orchestration when an instance changes state. -- Route host machine alerts into your monitoring stack. - Mirror billing or account events into your own tools. - Build an audit trail outside the Vast console. See [Notification Webhooks](/guides/reference/notification-webhooks) for setup, payloads, signing, retries, and API examples. -## Programmatic Access - -Use these endpoints when building your own notification settings UI or automation: - -| Endpoint | Purpose | -| --- | --- | -| `GET /api/v0/notification-types/` | Discover available notification types, display names, and default channel settings | -| `GET /api/v0/users/{user_id}/notification-prefs/` | Read the authenticated user's notification preferences | -| `PUT /api/v0/users/{user_id}/notification-prefs/` | Replace notification preferences | -| `GET /api/v0/notifications/inbox/` | Read the current in-app notification inbox | -| `PUT /api/v0/notifications/inbox/` | Mark in-app notifications as seen using a timestamp returned by the API | -| `GET /api/v0/webhooks/` | List notification webhooks | -| `POST /api/v0/webhooks/` | Create a notification webhook | - -All of these endpoints require an API key: - -```bash -curl -H "Authorization: Bearer $VAST_API_KEY" \ - "https://console.vast.ai/api/v0/notification-types/" -``` - ## Notification Type Keys -Notification types are identified by a `key`, such as `client:low_credit` or `host:machine_offline`. - -Use the full `key` when subscribing webhooks or updating preferences. Some events can exist in both renter and host contexts, so the full key avoids ambiguity. - -```json -{ - "key": "client:low_credit", - "slug": "low_credit", - "context": "client", - "topic": "billing", - "display_name": "Low balance notifications", - "default_preferences": { - "email": true, - "webhooks": false - }, - "mandatory_email": false -} -``` +Notification types are identified by a `key` with a context prefix. Renter events use the `client:` prefix, such as `client:low_credit` or `client:outbid`. -## In-App Inbox +Use the full `key` when subscribing webhooks or updating preferences. Because a similar event can exist for both renters and hosts, the full key avoids ambiguity. For the complete list of types, display names, and default channel settings, call [`GET /notification-types/`](/api-reference/notifications/list-notification-types). -The console can show an in-app notification inbox for supported events. The inbox is a capped, expiring list of recent notifications. It is useful for status and workflow events, but email and webhooks are the durable channels for messages you need to retain outside the console. +## In-App Inbox -Read the inbox: +The console can show an in-app notification inbox for supported events. The inbox holds a limited number of recent notifications, and older entries drop off over time. It is useful for status and workflow events, but email and webhooks are the durable channels for messages you need to retain outside the console. -```bash -curl -H "Authorization: Bearer $VAST_API_KEY" \ - "https://console.vast.ai/api/v0/notifications/inbox/" -``` +To read or mark the inbox programmatically, see the [Notifications API](/api-reference/notifications/get-notification-inbox). -The response includes `seen_through_at`. Send that exact value back when marking the batch as seen: +## Programmatic Access -```bash -curl -X PUT "https://console.vast.ai/api/v0/notifications/inbox/" \ - -H "Authorization: Bearer $VAST_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"seen_through_at": 1772490000.123}' -``` +Building your own notification settings UI or automation? The full set of notification, preference, inbox, and webhook endpoints is documented in the [Notifications API](/api-reference/notifications/list-notification-types). ## Good Defaults @@ -137,7 +85,6 @@ Start with this setup: | --- | --- | | Renting GPU instances manually | Keep low balance, outbid, downtime, and contract end date emails enabled | | Running automated workloads | Add a webhook for instance lifecycle, downtime, outbid, and billing events | -| Hosting machines | Keep machine error, verification, report, maintenance, and low-disk alerts enabled | | Building your own platform on Vast.ai | Use notification type keys, verify webhook signatures, and deduplicate by `event_id` | -Review the settings periodically, especially after adding hosted machines, enabling autobilling, or connecting a new automation workflow. +Review the settings periodically, especially after enabling autobilling or connecting a new automation workflow. diff --git a/host/notifications.mdx b/host/notifications.mdx new file mode 100644 index 00000000..314839d6 --- /dev/null +++ b/host/notifications.mdx @@ -0,0 +1,63 @@ +--- +title: "Host Notifications" +sidebarTitle: "Notifications and Webhooks" +description: "Choose which Vast.ai host machine, verification, and maintenance events reach you by email, in the console, or through webhooks." +"canonical": "/host/notifications" +--- + +import NotificationChannels from '/snippets/notifications/channels.mdx'; + +If you provide machines on Vast.ai, host notifications keep you informed about machine health, verification, renter reports, and maintenance. You can receive them by email, in the console, or through webhooks that feed your own monitoring and incident systems. + +The notification system is shared across the web console and the API. The console gives you a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, inbox, and webhook tools. + + +This page covers host notifications. For renter events such as low balance and instance lifecycle, see [Notifications](/guides/reference/notifications). + + +## Where to Find Notification Settings + +Open [Account Settings](https://cloud.vast.ai/account/) and go to **Notification Settings**. Host events appear only when you provide machines on Vast.ai. + +The **Host** group covers: + +| Group | What it covers | +| --- | --- | +| **Host** | Hosted machine health, verification failures, renter reports, maintenance, host disk warnings, and host-side contract events | + + + +## Update Host Notifications in the Console + +1. Open **Account Settings**. +2. Go to **Notification Settings**. +3. Review the **Host** section. +4. Turn off email for any optional event you do not want in your inbox. +5. Select host events that should be sent to a webhook, then create or edit the webhook destination. +6. Click **Save**. + +## Route Host Alerts to a Webhook + +Webhooks are useful for turning host alerts into actions, such as paging an operator when a hosted machine fails verification or goes offline, or routing host machine alerts into your monitoring stack. + +A common host action mapping points each host event at its machine in the console, for example: + +| Notification type | Console destination | +| --- | --- | +| `machine_offline` | `https://cloud.vast.ai/host/machines/` | +| `machine_error` | `https://cloud.vast.ai/host/machines/` | +| `maintenance_scheduled` | `https://cloud.vast.ai/host/machines/` | + +See [Notification Webhooks](/guides/reference/notification-webhooks) for setup, payloads, signing, retries, and API examples. + +## Notification Type Keys + +Notification types are identified by a `key` with a context prefix. Host events use the `host:` prefix, such as `host:machine_offline`. + +Use the full `key` when subscribing webhooks or updating preferences. Because a similar event can exist for both renters and hosts, the full key avoids ambiguity. For the complete list of types, display names, and default channel settings, call [`GET /notification-types/`](/api-reference/notifications/list-notification-types). + +## Good Defaults + +If you are hosting machines, keep machine error, verification, report, maintenance, and low-disk alerts enabled. + +Review the settings periodically, especially after adding hosted machines or connecting a new automation workflow. diff --git a/snippets/notifications/channels.mdx b/snippets/notifications/channels.mdx new file mode 100644 index 00000000..c6f1be43 --- /dev/null +++ b/snippets/notifications/channels.mdx @@ -0,0 +1,15 @@ +## How Delivery Works + +Each notification type can be delivered over one or more channels: + +| Channel | Use it for | +| --- | --- | +| **Email** | Human-readable messages for events you want in your inbox | +| **In-app** | Console notification inbox entries for supported events | +| **Webhooks** | Machine-readable HTTP events for your own systems | + +Some emails are mandatory because they are tied to account access, billing protection, or imminent service disruption. Mandatory email notifications cannot be fully disabled from the settings page. + + +Email and webhook preferences are set per notification type. The in-app inbox is shown automatically for supported events, so it is not a per-type preference and does not appear in `default_preferences`. + From 3e85f340e18d0780a9713d7c94fec3f896c76183 Mon Sep 17 00:00:00 2001 From: Abiola Akinnubi Date: Wed, 1 Jul 2026 12:44:35 -0700 Subject: [PATCH 04/26] Fix ordering of example webhooks --- .../notifications/google-chat-webhook.mdx | 88 +++++++++++-------- examples/notifications/slack-webhook.mdx | 88 +++++++++++-------- 2 files changed, 106 insertions(+), 70 deletions(-) diff --git a/examples/notifications/google-chat-webhook.mdx b/examples/notifications/google-chat-webhook.mdx index 209f2c5f..92b753d7 100644 --- a/examples/notifications/google-chat-webhook.mdx +++ b/examples/notifications/google-chat-webhook.mdx @@ -174,44 +174,25 @@ if __name__ == "__main__": ThreadingHTTPServer(("127.0.0.1", PORT), Handler).serve_forever() ``` -## Expose the Adapter Over HTTPS - -Vast.ai requires an HTTPS webhook URL. This example uses `tmole`; if you use another tunnel, run the equivalent command and copy the HTTPS URL it prints. +## Create the Vast.ai Webhook -In one terminal, start a tunnel to the adapter port: +From here you use three terminals, all in the `vast-google-chat-notifications` directory: the **adapter terminal** you just used, a **control terminal** for Vast.ai API calls, and a **tunnel terminal** for `tmole`. -```bash -tmole 8787 -``` +Create the webhook first. The adapter needs this webhook's signing secret before it can start, and `tmole` only starts once the adapter is already listening — so you create the webhook with a temporary URL now and point it at the tunnel once the tunnel is up. -Copy the HTTPS URL printed by the tunnel and export it in another terminal: +In the **control terminal**, set your Vast.ai API key and create the webhook: ```bash -read -rp "Public HTTPS webhook URL: " PUBLIC_WEBHOOK_URL -export PUBLIC_WEBHOOK_URL -``` - -## Create the Vast.ai Webhook - -Set your real Vast.ai API key and Google Chat webhook URL: +cd vast-google-chat-notifications -```bash read -rsp "Vast.ai API key: " VAST_API_KEY export VAST_API_KEY echo -read -rsp "Google Chat webhook URL: " GOOGLE_CHAT_URL -export GOOGLE_CHAT_URL -echo -``` - -Create a Vast.ai webhook that sends common billing and instance events to your tunnel URL: - -```bash cat > create-webhook.json < -Store `VAST_WEBHOOK_SECRET` securely. Vast.ai returns it when the webhook is created and when the secret is rotated. Normal list and update responses do not reveal it. +The signing secret (`webhook_secret`) is saved in `vast-webhook.json`. Store it securely — Vast.ai returns it only when the webhook is created or its secret is rotated, not on list or update responses. You can create the same webhook from the console, but the API flow is best for this example because the adapter needs the signing secret. @@ -254,17 +231,58 @@ You can create the same webhook from the console, but the API flow is best for t ## Start the Adapter -Start the adapter in the terminal where `GOOGLE_CHAT_URL` and `VAST_WEBHOOK_SECRET` are exported: +In the **adapter terminal** (where the virtual environment is active), load the signing secret from `vast-webhook.json`, set your Google Chat webhook URL, and start the adapter: ```bash +export VAST_WEBHOOK_SECRET="$( + python3 -c 'import json; print(json.load(open("vast-webhook.json"))["webhook"]["webhook_secret"])' +)" + +read -rsp "Google Chat webhook URL: " GOOGLE_CHAT_URL +export GOOGLE_CHAT_URL +echo + python3 vast_google_chat_adapter.py ``` -The adapter listens only on `127.0.0.1`. The HTTPS tunnel forwards public Vast.ai webhook deliveries to that local port. +The adapter prints `listening on http://127.0.0.1:8787` and listens only on `127.0.0.1`. Leave it running; the HTTPS tunnel you start next forwards public Vast.ai deliveries to that local port. + +## Expose the Adapter Over HTTPS + +Vast.ai requires an HTTPS webhook URL. This example uses `tmole`; if you use another tunnel, run the equivalent command and copy the HTTPS URL it prints. + +Because `tmole` only starts once something is listening on the port, start it now that the adapter is running. In the **tunnel terminal**: + +```bash +tmole 8787 +``` + +Copy the HTTPS URL it prints, such as `https://xxxxxx.tunnelmole.net`. + +## Point the Webhook at Your Tunnel + +In the **control terminal**, set the tunnel URL and update the webhook so Vast.ai delivers to it: + +```bash +read -rp "Public HTTPS webhook URL: " PUBLIC_WEBHOOK_URL +export PUBLIC_WEBHOOK_URL + +cat > update-webhook.json < create-webhook.json < -Store `VAST_WEBHOOK_SECRET` securely. Vast.ai returns it when the webhook is created and when the secret is rotated. Normal list and update responses do not reveal it. +The signing secret (`webhook_secret`) is saved in `vast-webhook.json`. Store it securely — Vast.ai returns it only when the webhook is created or its secret is rotated, not on list or update responses. You can create the same webhook from the console, but the API flow is best for this example because the adapter needs the signing secret. @@ -259,17 +236,58 @@ You can create the same webhook from the console, but the API flow is best for t ## Start the Adapter -Start the adapter in the terminal where `SLACK_WEBHOOK_URL` and `VAST_WEBHOOK_SECRET` are exported: +In the **adapter terminal** (where the virtual environment is active), load the signing secret from `vast-webhook.json`, set your Slack webhook URL, and start the adapter: ```bash +export VAST_WEBHOOK_SECRET="$( + python3 -c 'import json; print(json.load(open("vast-webhook.json"))["webhook"]["webhook_secret"])' +)" + +read -rsp "Slack incoming webhook URL: " SLACK_WEBHOOK_URL +export SLACK_WEBHOOK_URL +echo + python3 vast_slack_adapter.py ``` -The adapter listens only on `127.0.0.1`. The HTTPS tunnel forwards public Vast.ai webhook deliveries to that local port. +The adapter prints `listening on http://127.0.0.1:8787` and listens only on `127.0.0.1`. Leave it running; the HTTPS tunnel you start next forwards public Vast.ai deliveries to that local port. + +## Expose the Adapter Over HTTPS + +Vast.ai requires an HTTPS webhook URL. This example uses `tmole`; if you use another tunnel, run the equivalent command and copy the HTTPS URL it prints. + +Because `tmole` only starts once something is listening on the port, start it now that the adapter is running. In the **tunnel terminal**: + +```bash +tmole 8787 +``` + +Copy the HTTPS URL it prints, such as `https://xxxxxx.tunnelmole.net`. + +## Point the Webhook at Your Tunnel + +In the **control terminal**, set the tunnel URL and update the webhook so Vast.ai delivers to it: + +```bash +read -rp "Public HTTPS webhook URL: " PUBLIC_WEBHOOK_URL +export PUBLIC_WEBHOOK_URL + +cat > update-webhook.json < Date: Wed, 1 Jul 2026 14:29:35 -0700 Subject: [PATCH 05/26] hide in-app for now pending frontend catch-up --- api-reference/introduction.mdx | 2 +- api-reference/openapi.yaml | 108 --------- api-reference/openapi/yaml/notifications.yaml | 216 +++++++++--------- guides/reference/notification-webhooks.mdx | 4 +- guides/reference/notifications.mdx | 16 +- host/notifications.mdx | 6 +- snippets/notifications/channels.mdx | 6 +- 7 files changed, 125 insertions(+), 233 deletions(-) diff --git a/api-reference/introduction.mdx b/api-reference/introduction.mdx index be6f70bc..572529c0 100644 --- a/api-reference/introduction.mdx +++ b/api-reference/introduction.mdx @@ -23,7 +23,7 @@ New to the API? Start with the [API Hello World](/api-reference/hello-world), it | [Rate limits & errors](/api-reference/rate-limits-and-errors) | Per-endpoint limits, error codes, retry guidance | | [Creating instances](/api-reference/creating-instances-with-api) | Search-and-rent flow, configuration options | | [Templates](/api-reference/creating-and-using-templates-with-api) | Template fields, creation, instance launch | -| [Notifications](/api-reference/notifications/list-notification-types) | Notification preferences, in-app notifications, and webhooks | +| [Notifications](/api-reference/notifications/list-notification-types) | Notification preferences and webhooks | | Endpoints | Full OpenAPI reference for every endpoint | ## Base URL diff --git a/api-reference/openapi.yaml b/api-reference/openapi.yaml index 79df980e..ea2a1b6c 100644 --- a/api-reference/openapi.yaml +++ b/api-reference/openapi.yaml @@ -5113,95 +5113,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /api/v0/notifications/inbox/: - get: - summary: get notification inbox - description: Read recent in-app notifications for the authenticated user. - security: - - BearerAuth: [] - tags: - - Notifications - responses: - '200': - description: Inbox returned successfully - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: true - notifications: - type: array - items: - $ref: '#/components/schemas/InAppNotification' - last_seen_at: - type: number - format: float - nullable: true - seen_through_at: - type: number - format: float - nullable: true - unread_count: - type: integer - example: 2 - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - summary: mark notification inbox as seen - description: Mark in-app notifications as seen using a timestamp returned by - the inbox endpoint. - security: - - BearerAuth: [] - tags: - - Notifications - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - seen_through_at - properties: - seen_through_at: - type: number - format: float - description: Timestamp returned by `GET /api/v0/notifications/inbox/`. - example: - seen_through_at: 1772490000.123 - responses: - '200': - description: Inbox marked seen successfully - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: true - last_seen_at: - type: number - format: float - '400': - description: Bad Request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' /api/v0/webhooks/: get: summary: list notification webhooks @@ -9340,25 +9251,6 @@ components: low_credit: email: true webhooks: false - InAppNotification: - type: object - properties: - event_id: - type: string - example: 7e9a2c4e6f9e4a24a53b77c2d8e3f0aa - notif_type: - type: string - example: low_credit - subject: - type: string - example: Warning - Your Vast.ai Credit Balance Is Getting Low - message: - type: string - example: Your Vast.ai balance is below your configured threshold. - timestamp: - type: number - format: float - example: 1772490000.123 NotificationWebhook: type: object properties: diff --git a/api-reference/openapi/yaml/notifications.yaml b/api-reference/openapi/yaml/notifications.yaml index 2d332a46..021c51b3 100644 --- a/api-reference/openapi/yaml/notifications.yaml +++ b/api-reference/openapi/yaml/notifications.yaml @@ -127,94 +127,95 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /api/v0/notifications/inbox/: - get: - summary: get notification inbox - description: Read recent in-app notifications for the authenticated user. - security: - - BearerAuth: [] - tags: - - Notifications - responses: - '200': - description: Inbox returned successfully - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: true - notifications: - type: array - items: - $ref: '#/components/schemas/InAppNotification' - last_seen_at: - type: number - format: float - nullable: true - seen_through_at: - type: number - format: float - nullable: true - unread_count: - type: integer - example: 2 - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - summary: mark notification inbox as seen - description: Mark in-app notifications as seen using a timestamp returned by the inbox endpoint. - security: - - BearerAuth: [] - tags: - - Notifications - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - seen_through_at - properties: - seen_through_at: - type: number - format: float - description: Timestamp returned by `GET /api/v0/notifications/inbox/`. - example: - seen_through_at: 1772490000.123 - responses: - '200': - description: Inbox marked seen successfully - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: true - last_seen_at: - type: number - format: float - '400': - description: Bad Request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + # HIDDEN: in-app notification inbox endpoints — no console UI yet. Uncomment this block and run `npm run build-openapi` to re-enable. +# /api/v0/notifications/inbox/: +# get: +# summary: get notification inbox +# description: Read recent in-app notifications for the authenticated user. +# security: +# - BearerAuth: [] +# tags: +# - Notifications +# responses: +# '200': +# description: Inbox returned successfully +# content: +# application/json: +# schema: +# type: object +# properties: +# success: +# type: boolean +# example: true +# notifications: +# type: array +# items: +# $ref: '#/components/schemas/InAppNotification' +# last_seen_at: +# type: number +# format: float +# nullable: true +# seen_through_at: +# type: number +# format: float +# nullable: true +# unread_count: +# type: integer +# example: 2 +# '401': +# description: Unauthorized +# content: +# application/json: +# schema: +# $ref: '#/components/schemas/Error' +# put: +# summary: mark notification inbox as seen +# description: Mark in-app notifications as seen using a timestamp returned by the inbox endpoint. +# security: +# - BearerAuth: [] +# tags: +# - Notifications +# requestBody: +# required: true +# content: +# application/json: +# schema: +# type: object +# required: +# - seen_through_at +# properties: +# seen_through_at: +# type: number +# format: float +# description: Timestamp returned by `GET /api/v0/notifications/inbox/`. +# example: +# seen_through_at: 1772490000.123 +# responses: +# '200': +# description: Inbox marked seen successfully +# content: +# application/json: +# schema: +# type: object +# properties: +# success: +# type: boolean +# example: true +# last_seen_at: +# type: number +# format: float +# '400': +# description: Bad Request +# content: +# application/json: +# schema: +# $ref: '#/components/schemas/Error' +# '401': +# description: Unauthorized +# content: +# application/json: +# schema: +# $ref: '#/components/schemas/Error' /api/v0/webhooks/: get: summary: list notification webhooks @@ -535,25 +536,26 @@ components: low_credit: email: true webhooks: false - InAppNotification: - type: object - properties: - event_id: - type: string - example: 7e9a2c4e6f9e4a24a53b77c2d8e3f0aa - notif_type: - type: string - example: low_credit - subject: - type: string - example: Warning - Your Vast.ai Credit Balance Is Getting Low - message: - type: string - example: Your Vast.ai balance is below your configured threshold. - timestamp: - type: number - format: float - example: 1772490000.123 + # HIDDEN: InAppNotification schema (used only by the hidden inbox endpoints). Uncomment with the inbox block above to re-enable. +# InAppNotification: +# type: object +# properties: +# event_id: +# type: string +# example: 7e9a2c4e6f9e4a24a53b77c2d8e3f0aa +# notif_type: +# type: string +# example: low_credit +# subject: +# type: string +# example: Warning - Your Vast.ai Credit Balance Is Getting Low +# message: +# type: string +# example: Your Vast.ai balance is below your configured threshold. +# timestamp: +# type: number +# format: float +# example: 1772490000.123 NotificationWebhook: type: object properties: diff --git a/guides/reference/notification-webhooks.mdx b/guides/reference/notification-webhooks.mdx index 5c78c6aa..71acfec4 100644 --- a/guides/reference/notification-webhooks.mdx +++ b/guides/reference/notification-webhooks.mdx @@ -5,7 +5,7 @@ description: "Send Vast.ai notification events to your own HTTPS endpoint for au "canonical": "/guides/reference/notification-webhooks" --- -Notification webhooks deliver Vast.ai notification events to an HTTPS endpoint you operate. They are grouped under the notification system because they use the same notification types and preferences as email and in-app notifications. +Notification webhooks deliver Vast.ai notification events to an HTTPS endpoint you operate. They are grouped under the notification system because they use the same notification types and preferences as email notifications. Use webhooks when a notification should become an action: update your dashboard when an instance starts, pause automation when a low-balance event fires, or trigger cleanup when an instance is outbid. The same mechanics apply to host events — see [Host Notifications](/host/notifications). @@ -53,7 +53,7 @@ The signing secret is returned only when a webhook is created and when the secre -The test endpoint sends a `webhook_test` event with the same request format and signature headers as a real delivery. It does not create an inbox notification and is not retried. +The test endpoint sends a `webhook_test` event with the same request format and signature headers as a real delivery. It is not retried. ## Webhook Limits and Validation diff --git a/guides/reference/notifications.mdx b/guides/reference/notifications.mdx index 0acc7b8c..b83b6bff 100644 --- a/guides/reference/notifications.mdx +++ b/guides/reference/notifications.mdx @@ -1,15 +1,15 @@ --- title: "Notifications" sidebarTitle: "Notifications" -description: "Choose which Vast.ai account, billing, and instance events reach you by email, in the console, or through webhooks." +description: "Choose which Vast.ai account, billing, and instance events reach you by email or through webhooks." "canonical": "/guides/reference/notifications" --- import NotificationChannels from '/snippets/notifications/channels.mdx'; -Notifications let you decide which Vast.ai events you want to be told about, and where those messages go. A renter may only need low-balance and instance lifecycle alerts in the console. An automated platform can subscribe to the same events through webhooks and route them into its own incident, billing, or orchestration system. +Notifications let you decide which Vast.ai events you want to be told about, and where those messages go. A renter may only need low-balance and instance lifecycle alerts by email. An automated platform can subscribe to the same events through webhooks and route them into its own incident, billing, or orchestration system. -The notification system is shared across the web console and the API. The console gives you a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, inbox, and webhook tools. +The notification system is shared across the web console and the API. The console gives you a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, and webhook tools. This page covers renter notifications. If you also provide machines on Vast.ai, see [Host Notifications](/host/notifications) for host machine, verification, and maintenance events. @@ -51,7 +51,7 @@ Set your low-balance threshold below your autobilling threshold. That gives you ## Webhooks -Webhooks are part of the notification system. They use the same event catalog and preferences as email and in-app notifications, but deliver events to an HTTPS endpoint that you operate. +Webhooks are part of the notification system. They use the same event catalog and preferences as email notifications, but deliver events to an HTTPS endpoint that you operate. Use webhooks when you want to: @@ -67,15 +67,11 @@ Notification types are identified by a `key` with a context prefix. Renter event Use the full `key` when subscribing webhooks or updating preferences. Because a similar event can exist for both renters and hosts, the full key avoids ambiguity. For the complete list of types, display names, and default channel settings, call [`GET /notification-types/`](/api-reference/notifications/list-notification-types). -## In-App Inbox - -The console can show an in-app notification inbox for supported events. The inbox holds a limited number of recent notifications, and older entries drop off over time. It is useful for status and workflow events, but email and webhooks are the durable channels for messages you need to retain outside the console. - -To read or mark the inbox programmatically, see the [Notifications API](/api-reference/notifications/get-notification-inbox). +{/* HIDDEN: In-App Inbox section — no console notification inbox UI yet; restore when it ships. Original text: "The console can show an in-app notification inbox for supported events. The inbox holds a limited number of recent notifications, and older entries drop off over time. It is useful for status and workflow events, but email and webhooks are the durable channels for messages you need to retain outside the console. To read or mark the inbox programmatically, see /api-reference/notifications/get-notification-inbox." */} ## Programmatic Access -Building your own notification settings UI or automation? The full set of notification, preference, inbox, and webhook endpoints is documented in the [Notifications API](/api-reference/notifications/list-notification-types). +Building your own notification settings UI or automation? The full set of notification, preference, and webhook endpoints is documented in the [Notifications API](/api-reference/notifications/list-notification-types). ## Good Defaults diff --git a/host/notifications.mdx b/host/notifications.mdx index 314839d6..4bcb0e80 100644 --- a/host/notifications.mdx +++ b/host/notifications.mdx @@ -1,15 +1,15 @@ --- title: "Host Notifications" sidebarTitle: "Notifications and Webhooks" -description: "Choose which Vast.ai host machine, verification, and maintenance events reach you by email, in the console, or through webhooks." +description: "Choose which Vast.ai host machine, verification, and maintenance events reach you by email or through webhooks." "canonical": "/host/notifications" --- import NotificationChannels from '/snippets/notifications/channels.mdx'; -If you provide machines on Vast.ai, host notifications keep you informed about machine health, verification, renter reports, and maintenance. You can receive them by email, in the console, or through webhooks that feed your own monitoring and incident systems. +If you provide machines on Vast.ai, host notifications keep you informed about machine health, verification, renter reports, and maintenance. You can receive them by email or through webhooks that feed your own monitoring and incident systems. -The notification system is shared across the web console and the API. The console gives you a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, inbox, and webhook tools. +The notification system is shared across the web console and the API. The console gives you a settings page for choosing events and destinations. The API gives developers access to the same notification types, preferences, and webhook tools. This page covers host notifications. For renter events such as low balance and instance lifecycle, see [Notifications](/guides/reference/notifications). diff --git a/snippets/notifications/channels.mdx b/snippets/notifications/channels.mdx index c6f1be43..470b9b86 100644 --- a/snippets/notifications/channels.mdx +++ b/snippets/notifications/channels.mdx @@ -2,14 +2,16 @@ Each notification type can be delivered over one or more channels: +{/* HIDDEN: in-app delivery row — no console notification inbox UI yet. Restore when it ships: | **In-app** | Console notification inbox entries for supported events | */} + | Channel | Use it for | | --- | --- | | **Email** | Human-readable messages for events you want in your inbox | -| **In-app** | Console notification inbox entries for supported events | | **Webhooks** | Machine-readable HTTP events for your own systems | Some emails are mandatory because they are tied to account access, billing protection, or imminent service disruption. Mandatory email notifications cannot be fully disabled from the settings page. -Email and webhook preferences are set per notification type. The in-app inbox is shown automatically for supported events, so it is not a per-type preference and does not appear in `default_preferences`. +Email and webhook preferences are set per notification type. +{/* HIDDEN: in-app inbox note — restore when the console inbox UI ships: The in-app inbox is shown automatically for supported events, so it is not a per-type preference and does not appear in `default_preferences`. */} From 87db3c81c8150b88ab3ceb1b97a293da9668f509 Mon Sep 17 00:00:00 2001 From: Aida Atagian Date: Mon, 6 Jul 2026 18:41:33 -0700 Subject: [PATCH 06/26] docs(QA-1212): clarify reserved instance early-cancellation refund fee Explains that cancelling early claws back the discount received plus a 5% fee, and corrects the worked example's math to match. --- guides/instances/choosing/reserved-instances.mdx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/guides/instances/choosing/reserved-instances.mdx b/guides/instances/choosing/reserved-instances.mdx index e42ce0c8..2caaee85 100644 --- a/guides/instances/choosing/reserved-instances.mdx +++ b/guides/instances/choosing/reserved-instances.mdx @@ -95,15 +95,11 @@ You can extend your reservation at any time: ## Refunds -You can cancel (destroy) a reserved / prepaid instance to get part of your deposit back. Refund = Remaining deposit **minus** total discount already received. +When you reserve a machine, you get a discounted rate upfront. If you cancel early, we claw back the discount you already received, plus a small **5% fee**. -**Example:** +**Example:** You reserved for \$576/month (vs \$720 on-demand). After 15 days you saved \$72 in discounts. If you cancel, that \$72 plus a 5% fee (\$3.60) is deducted from your refund, so you get back ~\$212 instead of \$288. -- On-demand: \$1/hr → \$720/month -- Reserved (1 month): \$576/month -- Cancel immediately → Refund = \$576 -- Cancel after 15 days → Remaining = \$288 → Refund = \$216 (after discount penalty) -- Cancel at the end → Refund = \$0 +The longer you run, the less you get back, at the end of the month there's nothing left to refund since you've used the full discount. You will see the refund on the Billing page -\> Invoices table. @@ -137,7 +133,7 @@ Yes, you can extend it anytime via the same discount badge in the Instances page ### What happens if I cancel / delete a reserved instance early? -You'll receive a partial refund of your unused pre-paid balance, minus the total discount received so far. The refund amount will be displayed in the delete instance modal and will also appear on the Billing page after you delete the instance. +You'll receive a partial refund of your unused pre-paid balance, minus the total discount received so far plus a 5% fee. The refund amount will be displayed in the delete instance modal and will also appear on the Billing page after you delete the instance. ![image.png](/images/image.png) From 5d974c3406af52ad4ab16b08c0ea2c0d84e7fcab Mon Sep 17 00:00:00 2001 From: Aida Atagian Date: Mon, 6 Jul 2026 18:46:39 -0700 Subject: [PATCH 07/26] docs(QA-1212): keep intro refund sentence, tighten fee wording --- guides/instances/choosing/reserved-instances.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/instances/choosing/reserved-instances.mdx b/guides/instances/choosing/reserved-instances.mdx index 2caaee85..347b8861 100644 --- a/guides/instances/choosing/reserved-instances.mdx +++ b/guides/instances/choosing/reserved-instances.mdx @@ -95,7 +95,7 @@ You can extend your reservation at any time: ## Refunds -When you reserve a machine, you get a discounted rate upfront. If you cancel early, we claw back the discount you already received, plus a small **5% fee**. +You can cancel (destroy) a reserved / prepaid instance to get part of your deposit back. When you reserve a machine, you get a discounted rate upfront. If you cancel early, we claw back the discount you already received, plus a **5% fee**. **Example:** You reserved for \$576/month (vs \$720 on-demand). After 15 days you saved \$72 in discounts. If you cancel, that \$72 plus a 5% fee (\$3.60) is deducted from your refund, so you get back ~\$212 instead of \$288. From eb50ee2dc2564238cf96e2b1873176061a20065f Mon Sep 17 00:00:00 2001 From: Aida Atagian Date: Mon, 6 Jul 2026 18:47:20 -0700 Subject: [PATCH 08/26] docs(QA-1212): restore bullet-list example format with corrected fee math --- guides/instances/choosing/reserved-instances.mdx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/guides/instances/choosing/reserved-instances.mdx b/guides/instances/choosing/reserved-instances.mdx index 347b8861..abf2b3b1 100644 --- a/guides/instances/choosing/reserved-instances.mdx +++ b/guides/instances/choosing/reserved-instances.mdx @@ -97,9 +97,13 @@ You can extend your reservation at any time: You can cancel (destroy) a reserved / prepaid instance to get part of your deposit back. When you reserve a machine, you get a discounted rate upfront. If you cancel early, we claw back the discount you already received, plus a **5% fee**. -**Example:** You reserved for \$576/month (vs \$720 on-demand). After 15 days you saved \$72 in discounts. If you cancel, that \$72 plus a 5% fee (\$3.60) is deducted from your refund, so you get back ~\$212 instead of \$288. +**Example:** -The longer you run, the less you get back, at the end of the month there's nothing left to refund since you've used the full discount. +- On-demand: \$1/hr → \$720/month +- Reserved (1 month): \$576/month +- Cancel immediately → Refund = \$576 +- Cancel after 15 days → Remaining = \$288 → Refund = ~\$212 (after discount clawback + 5% fee) +- Cancel at the end → Refund = \$0 You will see the refund on the Billing page -\> Invoices table. From bdb909fe26899c1f7bcbe1f66500756558dff823 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Wed, 8 Jul 2026 12:32:41 +0200 Subject: [PATCH 09/26] CON-1584 add host account and CLI SDK bridge docs --- docs.json | 2 + host/account-hosting-agreement.mdx | 1 + host/account-security-for-hosts.mdx | 63 +++++++++++++++++++++++ host/cli-api-sdk.mdx | 80 +++++++++++++++++++++++++++++ host/fleet-operations.mdx | 4 +- host/hosting-overview.mdx | 2 + host/how-to-self-test.mdx | 4 +- host/quickstart.mdx | 2 +- 8 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 host/account-security-for-hosts.mdx create mode 100644 host/cli-api-sdk.mdx diff --git a/docs.json b/docs.json index 71380dd3..1fe85412 100644 --- a/docs.json +++ b/docs.json @@ -604,6 +604,7 @@ "pages": [ "host/quickstart", "host/account-hosting-agreement", + "host/account-security-for-hosts", "host/hardware-prep", "host/storage-setup", "host/network-ports", @@ -657,6 +658,7 @@ "group": "CLI", "icon": "terminal", "pages": [ + "host/cli-api-sdk", "host/cli/cleanup-machine", "host/cli/delete-machine", "host/cli/list-machine", diff --git a/host/account-hosting-agreement.mdx b/host/account-hosting-agreement.mdx index c8e5f150..9f7e6a5d 100644 --- a/host/account-hosting-agreement.mdx +++ b/host/account-hosting-agreement.mdx @@ -48,5 +48,6 @@ Copy the install command only from the official setup page while signed into the | Topic | Read next | | --- | --- | | First-time setup path | [Hosting Quickstart](/host/quickstart) | +| Host account security | [Account & Security for Hosts](/host/account-security-for-hosts) | | Installing the host software | [Installing Host Software](/host/installing-host-software) | | Agreement reference | [Hosting Agreement](/host/hosting-agreement) | diff --git a/host/account-security-for-hosts.mdx b/host/account-security-for-hosts.mdx new file mode 100644 index 00000000..e78c1686 --- /dev/null +++ b/host/account-security-for-hosts.mdx @@ -0,0 +1,63 @@ +--- +title: "Account & Security for Hosts" +sidebarTitle: "Account & Security" +description: "Host-specific pointers for account security, API keys, and teams." +"canonical": "/host/account-security-for-hosts" +personas: + - pro-operator + - headless-operator + - business-owner + - hobbyist +--- + +

+ +Use this page as the host-side map for account and access topics that live in the general Vast docs. + +Hosts use account credentials and API keys for setup, self-test, machine management, team operations, and automation. This page does not replace the canonical account docs; it points you to them with host-specific context. + +## Two-Factor Authentication + +Enable two-factor authentication on any account that can manage hosted machines, billing, payouts, API keys, or team roles. + +This is especially important for: + +- host accounts that own machines +- team owners and machine operators +- accounts that can create or rotate API keys +- accounts that can view earnings, invoices, or payout history + +For setup, recovery codes, SMS, authenticator apps, and troubleshooting, see [Two-Factor Authentication](/guides/reference/two-factor-authentication). + +## API Keys + +Hosts need API keys for CLI and SDK workflows such as self-test, listing and unlisting machines, pricing changes, maintenance windows, diagnostics, and fleet scripts. + +Create keys from the account context that owns the machines. If you use teams, create automation keys while operating in the intended team context rather than a personal account context. + +Treat host automation keys like production secrets: + +- scope permissions where possible +- store keys outside shell history and public scripts +- rotate keys if they are exposed +- remove keys that are no longer used + +For key creation, reset, deletion, and the Keys page, see [Keys](/guides/reference/keys). For CLI setup, see [CLI Authentication](/cli/authentication). + +## Teams For Host Operations + +Use a team when more than one person manages a hosting operation or when a business needs shared ownership and role-based access. + +The general Teams docs cover team creation, invitations, roles, and the Context Switcher. Start with [Teams Quickstart](/guides/teams/teams-quickstart). + +Before registering or managing machines for a team, confirm that you are operating in the intended account context. Machine operations, API keys, earnings, invoices, and payout settings should belong to the account or team that owns the hosted machines. + +## Related Pages + +| Topic | Read next | +| --- | --- | +| Host account setup | [Account & Hosting Agreement](/host/account-hosting-agreement) | +| CLI, API, and SDK for hosts | [Host CLI/API/SDK](/host/cli-api-sdk) | +| General key management | [Keys](/guides/reference/keys) | +| Two-factor authentication | [Two-Factor Authentication](/guides/reference/two-factor-authentication) | +| Teams setup | [Teams Quickstart](/guides/teams/teams-quickstart) | diff --git a/host/cli-api-sdk.mdx b/host/cli-api-sdk.mdx new file mode 100644 index 00000000..c260699f --- /dev/null +++ b/host/cli-api-sdk.mdx @@ -0,0 +1,80 @@ +--- +title: "Host CLI/API/SDK" +sidebarTitle: "CLI/API/SDK Intro" +description: "How hosts use the CLI, API, and SDK for self-test, machine management, pricing, maintenance, diagnostics, and fleet operations." +"canonical": "/host/cli-api-sdk" +personas: + - pro-operator + - headless-operator + - business-owner +--- + +
Pro OperatorHeadless / DCBusiness
+ +Hosts can use the Vast CLI, REST API, and Python SDK to operate machines without relying only on the console. + +This page is a host-specific orientation page. It links to the canonical CLI, API, and SDK setup docs instead of duplicating their install, authentication, and rate-limit details. + +## Start With Authentication + +Install and authenticate the CLI before using host commands: + +- [CLI Hello World](/cli/hello-world) +- [CLI Authentication](/cli/authentication) +- [CLI Rate Limits](/cli/rate-limits) + +For Python automation, start with: + +- [SDK Quickstart](/sdk/python/quickstart) +- [SDK Authentication](/sdk/python/authentication) +- [SDK Rate Limits](/sdk/python/rate-limits) + +For direct REST API integrations, start with: + +- [API Introduction](/api-reference/introduction) +- [API Authentication](/api-reference/authentication) +- [API Rate Limits and Errors](/api-reference/rate-limits-and-errors) + +Use an API key from the account or team that owns the hosted machines. For host-specific key guidance, see [Account & Security for Hosts](/host/account-security-for-hosts). + +## Common Host Workflows + +| Workflow | CLI reference | SDK reference | +| --- | --- | --- | +| Check machine state | [show machines](/host/cli/show-machines), [show machine](/host/cli/show-machine) | [show_machines](/host/sdk/show-machines), [show_machine](/host/sdk/show-machine) | +| List or unlist machines | [list machines](/host/cli/list-machines), [unlist machine](/host/cli/unlist-machine) | [list_machines](/host/sdk/list-machines), [unlist_machine](/host/sdk/unlist-machine) | +| Run self-test | [self-test machine](/host/cli/self-test-machine) | [self_test_machine](/host/sdk/self-test-machine) | +| Schedule maintenance | [schedule maint](/host/cli/schedule-maint), [cancel maint](/host/cli/cancel-maint), [show maints](/host/cli/show-maints) | [schedule_maint](/host/sdk/schedule-maint), [cancel_maint](/host/sdk/cancel-maint), [show_maints](/host/sdk/show-maints) | +| Adjust pricing | [set min-bid](/host/cli/set-min-bid) | [set_min_bid](/host/sdk/set-min-bid) | +| Configure default jobs | [set defjob](/host/cli/set-defjob), [remove defjob](/host/cli/remove-defjob) | [set_defjob](/host/sdk/set-defjob), [remove_defjob](/host/sdk/remove-defjob) | +| Repair or clean up | [cleanup machine](/host/cli/cleanup-machine), [defrag machines](/host/cli/defrag-machines) | [cleanup_machine](/host/sdk/cleanup-machine), [defrag_machines](/host/sdk/defrag-machines) | +| Market context | [metrics gpu](/host/cli/metrics-gpu), [metrics gpu-trends](/host/cli/metrics-gpu-trends), [metrics gpu-locations](/host/cli/metrics-gpu-locations) | Use the general SDK/API reference where available. | + +## Day-To-Day Host Usage + +Most hosts use the CLI or SDK for: + +- rerunning self-test after a fix +- checking whether machines are listed, rented, or reporting errors +- listing, unlisting, or repricing machines +- scheduling and canceling maintenance +- cleaning stale storage +- operating several machines from one workstation + +For examples that operate across many machines, see [Fleet Operations](/host/fleet-operations). + +## Automation Notes + +Use `--raw` for scripts so commands return structured output where supported. Use `--retry N` for unattended workflows that may hit transient rate limits. + +Do not paste API keys into shared scripts, tickets, screenshots, or public channels. If a key is exposed, rotate it from the [Keys page](/guides/reference/keys). + +## Related Pages + +| Topic | Read next | +| --- | --- | +| Host account and keys | [Account & Security for Hosts](/host/account-security-for-hosts) | +| Self-test workflow | [How to Self-Test](/host/how-to-self-test) | +| Multi-machine scripts | [Fleet Operations](/host/fleet-operations) | +| Full CLI host reference | [Host CLI commands](/host/cli/show-machines) | +| Full SDK host reference | [Host SDK methods](/host/sdk/show-machines) | diff --git a/host/fleet-operations.mdx b/host/fleet-operations.mdx index c5aa915a..e39e0faf 100644 --- a/host/fleet-operations.mdx +++ b/host/fleet-operations.mdx @@ -10,7 +10,7 @@ personas:
Headless / DCBusiness
-Use the CLI or SDK to manage many machines from one workstation. +Use the CLI or SDK to manage many machines from one workstation. For setup, authentication, and the host command map, see [Host CLI/API/SDK](/host/cli-api-sdk). If multiple people manage the fleet, run the machines under the intended team context and assign machine permissions deliberately. See [Host Teams](/host/host-teams). @@ -96,7 +96,9 @@ To decommission, unlist first, honor remaining rental end dates, then delete. Se | Topic | Read next | | --- | --- | | Headless install | [Headless Install](/host/headless-install) | +| Host CLI/API/SDK | [Host CLI/API/SDK](/host/cli-api-sdk) | | Maintenance | [Maintenance Windows](/host/maintenance-windows) | | Pricing | [Pricing Your Listing](/host/pricing-your-listing) | | Team operations | [Host Teams](/host/host-teams) | +| Account security and keys | [Account & Security for Hosts](/host/account-security-for-hosts) | | SDK automation | [SDK reference](/host/sdk/show-machines) | diff --git a/host/hosting-overview.mdx b/host/hosting-overview.mdx index bfec75a0..a6544f21 100644 --- a/host/hosting-overview.mdx +++ b/host/hosting-overview.mdx @@ -30,6 +30,8 @@ Create a dedicated host account from the [Vast host setup page](https://cloud.va After signup, use this docs path for hardware, storage, networking, install, and self-test details. For account conversion or Machines-tab troubleshooting, see [Account & Hosting Agreement](/host/account-hosting-agreement). +Before using API keys, CLI commands, SDK automation, or team access for hosting, review [Account & Security for Hosts](/host/account-security-for-hosts). + For setup, follow: 1. [Supported Hardware](/host/supported-hardware) diff --git a/host/how-to-self-test.mdx b/host/how-to-self-test.mdx index e8ae81d6..96c01981 100644 --- a/host/how-to-self-test.mdx +++ b/host/how-to-self-test.mdx @@ -19,8 +19,8 @@ Passing self-test makes a machine eligible for verification. It does not guarant ## Before You Run It -- Install the [Vast CLI](/cli/hello-world). -- Set the API key for the host-enabled account that owns the machine. +- Install the [Vast CLI](/cli/hello-world). For host-oriented command guidance, see [Host CLI/API/SDK](/host/cli-api-sdk). +- Set the API key for the host-enabled account that owns the machine. For host key guidance, see [Account & Security for Hosts](/host/account-security-for-hosts). - List the machine. - Make sure no client is actively renting it. - Run from outside the host LAN when testing ports, such as a cloud VM or mobile connection. Same-LAN tests can be confused by NAT hairpinning. diff --git a/host/quickstart.mdx b/host/quickstart.mdx index c449717a..35570668 100644 --- a/host/quickstart.mdx +++ b/host/quickstart.mdx @@ -14,7 +14,7 @@ Follow this ordered path for a first-time Vast host setup. Each step links to th ## Setup Path 1. **Decide whether Vast fits your hardware and expectations.** Read [Is Vast for Me?](/host/persona-decision-guide) and check your hardware against [Supported Hardware](/host/supported-hardware). -2. **Create a dedicated host account and accept the hosting agreement.** Start from the official [Vast host setup page](https://cloud.vast.ai/host/setup/), then see [Account & Hosting Agreement](/host/account-hosting-agreement) if the account flow gets stuck. +2. **Create a dedicated host account and accept the hosting agreement.** Start from the official [Vast host setup page](https://cloud.vast.ai/host/setup/), then see [Account & Hosting Agreement](/host/account-hosting-agreement) if the account flow gets stuck. Before using keys or automation, review [Account & Security for Hosts](/host/account-security-for-hosts). 3. **Prepare the machine.** Set up Ubuntu, drivers, and BIOS with [Hardware Prep](/host/hardware-prep), disks with [Storage Setup](/host/storage-setup), and port forwarding with [Network & Ports](/host/network-ports). 4. **Install the Vast host software with the Host Installer Wizard (TUI).** Copy the current command from the official [Vast host setup page](https://cloud.vast.ai/host/setup/), then use [Installing Host Software](/host/installing-host-software#host-installer-wizard) to understand the install flow. Running over SSH only? Use the [Headless Install Path](/host/headless-install). 5. **List the machine.** Set your price and terms with [Pricing Your Listing](/host/pricing-your-listing). From 4f7dee2c7e44a416f8f9a6283cd0ca20541382c6 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Wed, 8 Jul 2026 13:42:30 +0200 Subject: [PATCH 10/26] CON-1584 group host account docs --- docs.json | 10 ++++++++-- host/account-hosting-agreement.mdx | 6 +++--- host/account-security-for-hosts.mdx | 8 ++++---- host/cli-api-sdk.mdx | 4 ++-- host/fleet-operations.mdx | 2 +- host/hosting-overview.mdx | 4 ++-- host/how-to-self-test.mdx | 2 +- host/quickstart.mdx | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs.json b/docs.json index 1fe85412..7edc2b2a 100644 --- a/docs.json +++ b/docs.json @@ -598,13 +598,19 @@ "host/guide-to-taxes" ] }, + { + "group": "Account", + "icon": "user", + "pages": [ + "host/account-hosting-agreement", + "host/account-security-for-hosts" + ] + }, { "group": "Set Up", "icon": "wrench", "pages": [ "host/quickstart", - "host/account-hosting-agreement", - "host/account-security-for-hosts", "host/hardware-prep", "host/storage-setup", "host/network-ports", diff --git a/host/account-hosting-agreement.mdx b/host/account-hosting-agreement.mdx index 9f7e6a5d..922af876 100644 --- a/host/account-hosting-agreement.mdx +++ b/host/account-hosting-agreement.mdx @@ -1,6 +1,6 @@ --- -title: "Account & Hosting Agreement" -sidebarTitle: "Account & Agreement" +title: "Host Account and Agreement" +sidebarTitle: "Host Agreement" description: "Host account conversion and agreement flow." "canonical": "/host/account-hosting-agreement" personas: @@ -48,6 +48,6 @@ Copy the install command only from the official setup page while signed into the | Topic | Read next | | --- | --- | | First-time setup path | [Hosting Quickstart](/host/quickstart) | -| Host account security | [Account & Security for Hosts](/host/account-security-for-hosts) | +| Host account security | [Host Account Security](/host/account-security-for-hosts) | | Installing the host software | [Installing Host Software](/host/installing-host-software) | | Agreement reference | [Hosting Agreement](/host/hosting-agreement) | diff --git a/host/account-security-for-hosts.mdx b/host/account-security-for-hosts.mdx index e78c1686..dd36b28c 100644 --- a/host/account-security-for-hosts.mdx +++ b/host/account-security-for-hosts.mdx @@ -1,7 +1,7 @@ --- -title: "Account & Security for Hosts" -sidebarTitle: "Account & Security" -description: "Host-specific pointers for account security, API keys, and teams." +title: "Host Account Security" +sidebarTitle: "Security" +description: "Host-specific pointers for 2FA, API keys, and teams." "canonical": "/host/account-security-for-hosts" personas: - pro-operator @@ -56,7 +56,7 @@ Before registering or managing machines for a team, confirm that you are operati | Topic | Read next | | --- | --- | -| Host account setup | [Account & Hosting Agreement](/host/account-hosting-agreement) | +| Host account setup | [Host Account and Agreement](/host/account-hosting-agreement) | | CLI, API, and SDK for hosts | [Host CLI/API/SDK](/host/cli-api-sdk) | | General key management | [Keys](/guides/reference/keys) | | Two-factor authentication | [Two-Factor Authentication](/guides/reference/two-factor-authentication) | diff --git a/host/cli-api-sdk.mdx b/host/cli-api-sdk.mdx index c260699f..0475aca4 100644 --- a/host/cli-api-sdk.mdx +++ b/host/cli-api-sdk.mdx @@ -35,7 +35,7 @@ For direct REST API integrations, start with: - [API Authentication](/api-reference/authentication) - [API Rate Limits and Errors](/api-reference/rate-limits-and-errors) -Use an API key from the account or team that owns the hosted machines. For host-specific key guidance, see [Account & Security for Hosts](/host/account-security-for-hosts). +Use an API key from the account or team that owns the hosted machines. For host-specific key guidance, see [Host Account Security](/host/account-security-for-hosts). ## Common Host Workflows @@ -73,7 +73,7 @@ Do not paste API keys into shared scripts, tickets, screenshots, or public chann | Topic | Read next | | --- | --- | -| Host account and keys | [Account & Security for Hosts](/host/account-security-for-hosts) | +| Host account and keys | [Host Account Security](/host/account-security-for-hosts) | | Self-test workflow | [How to Self-Test](/host/how-to-self-test) | | Multi-machine scripts | [Fleet Operations](/host/fleet-operations) | | Full CLI host reference | [Host CLI commands](/host/cli/show-machines) | diff --git a/host/fleet-operations.mdx b/host/fleet-operations.mdx index e39e0faf..d07d8e10 100644 --- a/host/fleet-operations.mdx +++ b/host/fleet-operations.mdx @@ -100,5 +100,5 @@ To decommission, unlist first, honor remaining rental end dates, then delete. Se | Maintenance | [Maintenance Windows](/host/maintenance-windows) | | Pricing | [Pricing Your Listing](/host/pricing-your-listing) | | Team operations | [Host Teams](/host/host-teams) | -| Account security and keys | [Account & Security for Hosts](/host/account-security-for-hosts) | +| Account security and keys | [Host Account Security](/host/account-security-for-hosts) | | SDK automation | [SDK reference](/host/sdk/show-machines) | diff --git a/host/hosting-overview.mdx b/host/hosting-overview.mdx index a6544f21..21127800 100644 --- a/host/hosting-overview.mdx +++ b/host/hosting-overview.mdx @@ -28,9 +28,9 @@ Vast does not provide setup support for getting a machine working. For community Create a dedicated host account from the [Vast host setup page](https://cloud.vast.ai/host/setup/). That flow links to the hosting agreement and starts host onboarding, including access to the Machines area after the agreement is accepted. -After signup, use this docs path for hardware, storage, networking, install, and self-test details. For account conversion or Machines-tab troubleshooting, see [Account & Hosting Agreement](/host/account-hosting-agreement). +After signup, use this docs path for hardware, storage, networking, install, and self-test details. For account conversion or Machines-tab troubleshooting, see [Host Account and Agreement](/host/account-hosting-agreement). -Before using API keys, CLI commands, SDK automation, or team access for hosting, review [Account & Security for Hosts](/host/account-security-for-hosts). +Before using API keys, CLI commands, SDK automation, or team access for hosting, review [Host Account Security](/host/account-security-for-hosts). For setup, follow: diff --git a/host/how-to-self-test.mdx b/host/how-to-self-test.mdx index 96c01981..c6207d2f 100644 --- a/host/how-to-self-test.mdx +++ b/host/how-to-self-test.mdx @@ -20,7 +20,7 @@ Passing self-test makes a machine eligible for verification. It does not guarant ## Before You Run It - Install the [Vast CLI](/cli/hello-world). For host-oriented command guidance, see [Host CLI/API/SDK](/host/cli-api-sdk). -- Set the API key for the host-enabled account that owns the machine. For host key guidance, see [Account & Security for Hosts](/host/account-security-for-hosts). +- Set the API key for the host-enabled account that owns the machine. For host key guidance, see [Host Account Security](/host/account-security-for-hosts). - List the machine. - Make sure no client is actively renting it. - Run from outside the host LAN when testing ports, such as a cloud VM or mobile connection. Same-LAN tests can be confused by NAT hairpinning. diff --git a/host/quickstart.mdx b/host/quickstart.mdx index 35570668..fb0483c4 100644 --- a/host/quickstart.mdx +++ b/host/quickstart.mdx @@ -14,7 +14,7 @@ Follow this ordered path for a first-time Vast host setup. Each step links to th ## Setup Path 1. **Decide whether Vast fits your hardware and expectations.** Read [Is Vast for Me?](/host/persona-decision-guide) and check your hardware against [Supported Hardware](/host/supported-hardware). -2. **Create a dedicated host account and accept the hosting agreement.** Start from the official [Vast host setup page](https://cloud.vast.ai/host/setup/), then see [Account & Hosting Agreement](/host/account-hosting-agreement) if the account flow gets stuck. Before using keys or automation, review [Account & Security for Hosts](/host/account-security-for-hosts). +2. **Create a dedicated host account and accept the hosting agreement.** Start from the official [Vast host setup page](https://cloud.vast.ai/host/setup/), then see [Host Account and Agreement](/host/account-hosting-agreement) if the account flow gets stuck. Before using keys or automation, review [Host Account Security](/host/account-security-for-hosts). 3. **Prepare the machine.** Set up Ubuntu, drivers, and BIOS with [Hardware Prep](/host/hardware-prep), disks with [Storage Setup](/host/storage-setup), and port forwarding with [Network & Ports](/host/network-ports). 4. **Install the Vast host software with the Host Installer Wizard (TUI).** Copy the current command from the official [Vast host setup page](https://cloud.vast.ai/host/setup/), then use [Installing Host Software](/host/installing-host-software#host-installer-wizard) to understand the install flow. Running over SSH only? Use the [Headless Install Path](/host/headless-install). 5. **List the machine.** Set your price and terms with [Pricing Your Listing](/host/pricing-your-listing). From f81a44675bafe2ed2ca29b748a86f0c91c8760ef Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Wed, 8 Jul 2026 13:44:57 +0200 Subject: [PATCH 11/26] CON-1584 group self-test host nav pages --- docs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs.json b/docs.json index 7edc2b2a..cf8fea0f 100644 --- a/docs.json +++ b/docs.json @@ -627,8 +627,8 @@ "host/verification-stages", "host/understanding-verification", "host/how-to-self-test", - "host/market-metrics", "host/self-test-reference", + "host/market-metrics", "host/pricing-your-listing", "host/not-in-search" ] From b6a60c7be72b3482787d2441889821d87148d129 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Wed, 8 Jul 2026 13:48:30 +0200 Subject: [PATCH 12/26] CON-1584 reorder verify and list host nav --- docs.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs.json b/docs.json index cf8fea0f..344e4d0d 100644 --- a/docs.json +++ b/docs.json @@ -624,12 +624,12 @@ "icon": "badge-check", "pages": [ "host/optimization-guide", - "host/verification-stages", - "host/understanding-verification", "host/how-to-self-test", "host/self-test-reference", - "host/market-metrics", "host/pricing-your-listing", + "host/market-metrics", + "host/understanding-verification", + "host/verification-stages", "host/not-in-search" ] }, From bf9f3ea60206b1e38711321601b4dfff7e4bb42a Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Wed, 8 Jul 2026 13:50:44 +0200 Subject: [PATCH 13/26] CON-1584 link common questions to search troubleshooting --- host/common-host-questions.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/host/common-host-questions.mdx b/host/common-host-questions.mdx index d75e6f86..2e76bc27 100644 --- a/host/common-host-questions.mdx +++ b/host/common-host-questions.mdx @@ -14,6 +14,8 @@ personas: Use this page to route common host questions to the page that owns the answer. +If your machine is listed but renters cannot find it in marketplace search, start with [Why Isn't My Machine in Search?](/host/not-in-search). + ## Getting Started | Question | Read | @@ -48,6 +50,7 @@ Use this page to route common host questions to the page that owns the answer. | How do I test ports from outside my LAN? | [Network & Ports](/host/network-ports#test-ports-outside-lan) | | What does self-test check? | [How to Self-Test](/host/how-to-self-test#what-the-self-test-checks) | | What does `vericode=8` mean? | [Glossary](/host/glossary#vericode) and [Machine Error Reference](/host/machine-errors) | +| Why isn't my machine showing in marketplace search? | [Why Isn't My Machine in Search?](/host/not-in-search) | | Why is my machine not found, not rentable, or not in search? | [Why Isn't My Machine in Search?](/host/not-in-search) | | How does verification work? | [Understanding Verification](/host/understanding-verification) | From 0d129d08d397875a7b1d206ef26395452a55ecb2 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Wed, 8 Jul 2026 14:28:32 +0200 Subject: [PATCH 14/26] CON-1584 add host setup screenshots --- host/account-hosting-agreement.mdx | 4 ++++ host/installing-host-software.mdx | 6 +++++- images/host-console-host-navigation.webp | Bin 0 -> 7386 bytes images/host-setup-install-manager.webp | Bin 0 -> 44124 bytes 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 images/host-console-host-navigation.webp create mode 100644 images/host-setup-install-manager.webp diff --git a/host/account-hosting-agreement.mdx b/host/account-hosting-agreement.mdx index 922af876..391f939b 100644 --- a/host/account-hosting-agreement.mdx +++ b/host/account-hosting-agreement.mdx @@ -25,6 +25,10 @@ If a business or fleet needs multiple operators, use a dedicated host team accou Once your host account is created, open the [host setup page](https://cloud.vast.ai/host/setup/). There is a link in the first paragraph to the hosting agreement. Read through the agreement. Once you accept, your account is converted to a hosting account, and a Machines link appears in the navigation. Your account can now list machines that are running the daemon software. + + ![Host console sidebar with Machines, Market Stats, Create Job, and Setup under Hosting](/images/host-console-host-navigation.webp) + + The setup page is the canonical place for the current agreement link and generated install command. Do not rely on copied buttons, screenshots, or old setup instructions.
diff --git a/host/installing-host-software.mdx b/host/installing-host-software.mdx index 220d2614..be1ffef0 100644 --- a/host/installing-host-software.mdx +++ b/host/installing-host-software.mdx @@ -19,7 +19,7 @@ Install from the official Vast host setup flow, then confirm the daemon, Docker ## Before You Install -Complete [Hardware Prep](/host/hardware-prep), [Storage Setup](/host/storage-setup), and [Network & Ports](/host/network-ports). Your account must be host-enabled and the hosting agreement accepted; see [Account & Hosting Agreement](/host/account-hosting-agreement). +Complete [Hardware Prep](/host/hardware-prep), [Storage Setup](/host/storage-setup), and [Network & Ports](/host/network-ports). Your account must be host-enabled and the hosting agreement accepted; see [Host Account and Agreement](/host/account-hosting-agreement). ## Install Command @@ -30,6 +30,10 @@ The command can include an account-specific machine installation key. Do not reu For team-owned hosts, switch into the intended team context before opening the setup page or copying the command. See [Host Teams](/host/host-teams#install-in-the-right-context). + + ![Host setup page with the Install Manager step selected and setup notes visible](/images/host-setup-install-manager.webp) + + ## Host Installer Wizard diff --git a/images/host-console-host-navigation.webp b/images/host-console-host-navigation.webp new file mode 100644 index 0000000000000000000000000000000000000000..ff3652b3ab9e2c07f9138d9c9982ddaf809c79f2 GIT binary patch literal 7386 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 M?#2a!g57L@00mZfd;kCd literal 0 HcmV?d00001 diff --git a/images/host-setup-install-manager.webp b/images/host-setup-install-manager.webp new file mode 100644 index 0000000000000000000000000000000000000000..c6c39757c9d38adc9f16ac4f945cc1c3f3eb54bf GIT binary patch literal 44124 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

Uo{)l#!m&5k;Nu1;1oi{;z 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 literal 0 HcmV?d00001 From 6d2b1cbb81ec4e685c55f60f03425e0aa716daee Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Fri, 10 Jul 2026 11:35:17 +0200 Subject: [PATCH 15/26] CON-1584 add local review annotation server (temporary review aid) Reviewers run 'node review-server.mjs' next to 'npm run dev' and browse http://localhost:4000 to comment directly on pages; feedback exports as Jira-importable CSV/Markdown/JSON. To be dropped before merge. Co-Authored-By: Claude Fable 5 --- review-server.mjs | 1111 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1111 insertions(+) create mode 100644 review-server.mjs diff --git a/review-server.mjs b/review-server.mjs new file mode 100644 index 00000000..dd6a818b --- /dev/null +++ b/review-server.mjs @@ -0,0 +1,1111 @@ +#!/usr/bin/env node +/* + * Vast.ai docs review server — annotate the local Mintlify preview (PR #185) + * --------------------------------------------------------------------------- + * Zero dependencies. Node 18+. + * + * What it does: + * - Reverse-proxies the Mintlify dev preview (default http://localhost:3000) + * and injects a review overlay into every page. + * - Reviewers highlight text and leave comments; feedback autosaves to + * ./review-feedback/feedback-.json on this machine. + * - Exports Jira-importable CSV, plus Markdown and JSON, at any time. + * + * Usage: + * Terminal 1: npm run dev -- --no-open (Mintlify preview on :3000) + * Terminal 2: node review-server.mjs (review overlay on :4000) + * Browse: http://localhost:4000/host/hosting-overview + * + * Options: + * --port 4000 port for this review server + * --target http://localhost:3000 where the Mintlify preview runs + * --dir ./review-feedback where feedback JSON files are written + * + * Status & exports: http://localhost:4000/__review__/ + */ + +import http from 'node:http'; +import net from 'node:net'; +import fs from 'node:fs'; +import path from 'node:path'; +import crypto from 'node:crypto'; + +// ------------------------------------------------------------------ config +const argv = process.argv.slice(2); +function argValue(name, dflt) { + const i = argv.indexOf(name); + return i !== -1 && argv[i + 1] ? argv[i + 1] : dflt; +} +const PORT = parseInt(argValue('--port', '4000'), 10); +const TARGET = new URL(argValue('--target', 'http://localhost:3000')); +const FEEDBACK_DIR = path.resolve(argValue('--dir', './review-feedback')); +const PR_URL = 'https://github.com/vast-ai/docs/pull/185'; +const PR_LABEL = 'docs-pr185-review'; + +fs.mkdirSync(FEEDBACK_DIR, { recursive: true }); + +// ------------------------------------------------------------- feedback io +function reviewerSlug(name) { + const raw = String(name || '').trim(); + const s = raw.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); + // short hash keeps distinct names ("Ana B" vs "ana-b", non-ASCII names) in distinct files + const h = crypto.createHash('sha1').update(raw).digest('hex').slice(0, 6); + return (s || 'reviewer') + '-' + h; +} +function feedbackFile(reviewer) { + return path.join(FEEDBACK_DIR, `feedback-${reviewerSlug(reviewer)}.json`); +} +function readReviewerState(reviewer) { + try { + const raw = fs.readFileSync(feedbackFile(reviewer), 'utf8'); + const data = JSON.parse(raw); + return Array.isArray(data.items) ? data.items : []; + } catch { + return []; + } +} +function mergeById(existing, incoming) { + const byId = new Map(); + for (const it of existing) if (it && typeof it.id === 'string') byId.set(it.id, it); + for (const it of incoming) { + if (!it || typeof it.id !== 'string') continue; + const cur = byId.get(it.id); + if (!cur || String(it.updatedAt || '') >= String(cur.updatedAt || '')) byId.set(it.id, it); + } + return [...byId.values()]; +} +function writeReviewerState(reviewer, items) { + // per-item merge (newest updatedAt wins) so concurrent tabs/late writers never clobber each other + const merged = mergeById(readReviewerState(reviewer), items); + const file = feedbackFile(reviewer); + const payload = { reviewer, updatedAt: new Date().toISOString(), pr: PR_URL, items: merged }; + const tmp = file + '.tmp'; + fs.writeFileSync(tmp, JSON.stringify(payload, null, 2)); + fs.renameSync(tmp, file); + return merged.length; +} +function readAllItems() { + const byId = new Map(); + const reviewers = []; + for (const f of fs.readdirSync(FEEDBACK_DIR)) { + if (!/^feedback-.*\.json$/.test(f)) continue; + try { + const data = JSON.parse(fs.readFileSync(path.join(FEEDBACK_DIR, f), 'utf8')); + if (Array.isArray(data.items)) { + // dedupe across files by item id (a reviewer renaming mid-session leaves copies in + // both files) — keep the newest version, tombstones included so deletes win + for (const it of data.items) { + if (!it || typeof it.id !== 'string') continue; + const cur = byId.get(it.id); + if (!cur || String(it.updatedAt || '') >= String(cur.updatedAt || '')) byId.set(it.id, it); + } + reviewers.push(data.reviewer || f); + } + } catch { /* skip unreadable file */ } + } + const all = [...byId.values()].filter((it) => !it.deleted); + all.sort((a, b) => (a.page || '').localeCompare(b.page || '') || (a.createdAt || '').localeCompare(b.createdAt || '')); + return { items: all, reviewers }; +} + +// ---------------------------------------------------------------- exports +const PRIORITY_MAP = { Blocker: 'Highest', Major: 'High', Minor: 'Medium', Nit: 'Low' }; + +function csvCell(v) { + let s = String(v ?? ''); + // formula-injection guard; +/- only when they can start a formula/number so that + // ordinary quoted text like "--target http://..." is not corrupted by the apostrophe + if (/^[=@\t\r]/.test(s) || /^[+-][\d.=]/.test(s)) s = "'" + s; + if (/[",\r\n]/.test(s)) s = '"' + s.replace(/"/g, '""') + '"'; + return s; +} +function firstLine(s) { + return String(s || '').split(/\r?\n/)[0].trim(); +} +function itemSummary(it) { + const page = (it.page || '/').replace(/^\/host\//, '').replace(/^\//, '') || 'home'; + let head = firstLine(it.comment).slice(0, 90); + if (!head) head = (it.quote || '').slice(0, 90); + return `[${page}] ${it.category || 'Feedback'}: ${head}`; +} +function itemDescription(it) { + const lines = []; + lines.push(`Docs review feedback on PR 185 (${PR_URL})`); + lines.push(''); + lines.push(`Page: ${it.page || '/'}${it.pageTitle ? ' — ' + it.pageTitle : ''}`); + if (it.heading) lines.push(`Section: ${it.heading}`); + if (it.quote) { + lines.push(''); + lines.push('Quoted text:'); + lines.push(`"${it.quote}"`); + } + lines.push(''); + lines.push('Comment:'); + lines.push(it.comment || '(no comment)'); + lines.push(''); + lines.push(`Category: ${it.category || '-'} | Severity: ${it.severity || '-'} | Status: ${it.status || 'open'}`); + lines.push(`Reviewer: ${it.reviewer || '-'} | Created: ${it.createdAt || '-'}`); + return lines.join('\n'); +} +function toCsv(items) { + const header = ['Summary', 'Issue Type', 'Priority', 'Labels', 'Description', 'Page', 'Section', 'Quote', 'Category', 'Severity', 'Status', 'Reviewer', 'Created']; + const rows = [header.map(csvCell).join(',')]; + for (const it of items) { + rows.push([ + itemSummary(it), + 'Task', + PRIORITY_MAP[it.severity] || 'Medium', + PR_LABEL, + itemDescription(it), + it.page || '', + it.heading || '', + it.quote || '', + it.category || '', + it.severity || '', + it.status || 'open', + it.reviewer || '', + it.createdAt || '', + ].map(csvCell).join(',')); + } + return '' + rows.join('\r\n') + '\r\n'; +} +function toMarkdown(items, reviewers) { + const out = []; + out.push('# Vast.ai docs review feedback — PR 185'); + out.push(''); + out.push(`Generated ${new Date().toISOString()} · ${items.length} item(s) · reviewers: ${reviewers.join(', ') || '—'}`); + out.push(`PR: ${PR_URL}`); + const byPage = new Map(); + for (const it of items) { + const key = it.page || '/'; + if (!byPage.has(key)) byPage.set(key, []); + byPage.get(key).push(it); + } + for (const [page, list] of byPage) { + out.push(''); + out.push(`## ${page}${list[0].pageTitle ? ' — ' + list[0].pageTitle : ''}`); + let n = 0; + for (const it of list) { + n += 1; + out.push(''); + out.push(`### ${n}. [${it.severity || '-'} · ${it.category || '-'}]${it.heading ? ' ' + it.heading : ''}${it.status === 'resolved' ? ' (resolved)' : ''}`); + if (it.quote) out.push(`> ${String(it.quote).replace(/\r?\n/g, '\n> ')}`); + out.push(''); + out.push(it.comment || '(no comment)'); + out.push(''); + out.push(`*— ${it.reviewer || 'unknown'}, ${it.createdAt || ''}*`); + } + } + out.push(''); + return out.join('\n'); +} + +// ------------------------------------------------------------ status page +function esc(s) { + return String(s ?? '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} +function statusPage() { + const { items, reviewers } = readAllItems(); + const open = items.filter((i) => i.status !== 'resolved').length; + const byReviewer = {}; + for (const it of items) byReviewer[it.reviewer || '?'] = (byReviewer[it.reviewer || '?'] || 0) + 1; + const rows = Object.entries(byReviewer) + .map(([r, n]) => `${esc(r)}${n}`).join('') || 'No feedback yet'; + return `PR 185 review feedback + +

Vast.ai docs review — PR 185 feedback

+

${items.length} item(s), ${open} open. Feedback files live in ${esc(FEEDBACK_DIR)}.

+${rows}
ReviewerItems
+
+

When you're done reviewing, send back the CSV or the JSON file — either can be imported into Jira for record keeping.

+

← Back to the docs preview

+`; +} + +// ---------------------------------------------------------------- overlay +// Client-side overlay source. Served at /__review__/overlay.js. +// NOTE: written without backticks or "$"+"{" so it can live in String.raw. +const OVERLAY_JS = String.raw` +(function () { + 'use strict'; + if (window.__vastReviewLoaded) return; + window.__vastReviewLoaded = true; + + var API = '/__review__/api'; + var LS_REVIEWER = 'vastReview.reviewer'; + var LS_ITEMS = 'vastReview.items'; + var CATEGORIES = ['Error', 'Unclear', 'Missing', 'Outdated', 'Suggestion', 'Question', 'Praise']; + var SEVERITIES = ['Blocker', 'Major', 'Minor', 'Nit']; + var SEV_COLOR = { Blocker: '#d92d20', Major: '#e8590c', Minor: '#b58a00', Nit: '#5c677d' }; + + // ---------------- state ---------------- + var reviewer = ''; + var items = []; + var pending = null; // selection captured but not yet saved + var editingId = null; + var showAllPages = false; + var saveStatus = 'idle'; // idle | saving | saved | offline + var saveTimer = null; + var anchorTimer = null; + + try { reviewer = localStorage.getItem(LS_REVIEWER) || ''; } catch (e) {} + try { items = JSON.parse(localStorage.getItem(LS_ITEMS) || '[]'); } catch (e) { items = []; } + if (!Array.isArray(items)) items = []; + + function uid() { return 'fb-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8); } + function nowIso() { return new Date().toISOString(); } + function esc(s) { + return String(s == null ? '' : s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + } + function pageTitle() { + var t = document.title || ''; + return t.replace(/\s*[-|–—]\s*Vast.*$/i, '').trim() || t; + } + function visibleItems() { return items.filter(function (it) { return !it.deleted; }); } + function pageItems() { + return visibleItems().filter(function (it) { return it.page === location.pathname; }); + } + + // ---------------- persistence ---------------- + function persistLocal() { + try { localStorage.setItem(LS_ITEMS, JSON.stringify(items)); } catch (e) {} + } + function scheduleSave() { + persistLocal(); + renderBadge(); + if (saveTimer) clearTimeout(saveTimer); + saveStatus = 'saving'; + renderSaveStatus(); + saveTimer = setTimeout(pushToServer, 700); + } + function pushToServer() { + if (!reviewer) { saveStatus = 'offline'; renderSaveStatus(); return; } + fetch(API + '/state', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ reviewer: reviewer, items: items }) + }).then(function (r) { + saveStatus = r.ok ? 'saved' : 'offline'; + renderSaveStatus(); + }).catch(function () { + saveStatus = 'offline'; + renderSaveStatus(); + }); + } + function mergeServerState() { + if (!reviewer) return; + fetch(API + '/state?reviewer=' + encodeURIComponent(reviewer)) + .then(function (r) { return r.json(); }) + .then(function (data) { + var server = (data && Array.isArray(data.items)) ? data.items : []; + var byId = {}; + items.forEach(function (it) { byId[it.id] = it; }); + server.forEach(function (s) { + var mine = byId[s.id]; + if (!mine || String(s.updatedAt || '') > String(mine.updatedAt || '')) byId[s.id] = s; + }); + items = Object.keys(byId).map(function (k) { return byId[k]; }); + persistLocal(); + scheduleAnchor(); + renderAll(); + scheduleSave(); // push items that so far existed only in this browser back to disk + }) + .catch(function () {}); + } + function flushNow() { + // best-effort synchronous save on tab close / navigation away + if (!reviewer || !items.length) return; + if (saveTimer) { clearTimeout(saveTimer); saveTimer = null; } + persistLocal(); + try { + var blob = new Blob([JSON.stringify({ reviewer: reviewer, items: items })], { type: 'application/json' }); + navigator.sendBeacon(API + '/state', blob); + } catch (e) {} + } + window.addEventListener('pagehide', flushNow); + document.addEventListener('visibilitychange', function () { + if (document.visibilityState === 'hidden') flushNow(); + }); + + // ---------------- text index + anchoring ---------------- + function skipNode(el) { + if (!el) return false; + var tag = el.nodeName; + return tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT' || tag === 'TEMPLATE' || el.id === '__vast_review_host__'; + } + function buildTextIndex() { + var nodes = []; + var text = ''; + var visCache = new Map(); + function isRendered(el) { + if (visCache.has(el)) return visCache.get(el); + var ok = false; + if (el.getClientRects().length > 0) { + var r = el.getBoundingClientRect(); + // zero/1px boxes catch sr-only clip patterns as well as display:none + ok = r.width > 1.5 && r.height > 1.5 && getComputedStyle(el).visibility !== 'hidden'; + } + visCache.set(el, ok); + return ok; + } + var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { + acceptNode: function (node) { + var p = node.parentNode; + while (p && p !== document.body) { + if (p.nodeType === 1 && skipNode(p)) return NodeFilter.FILTER_REJECT; + p = p.parentNode; + } + var el = node.parentElement; + if (el && node.data.trim() && !isRendered(el)) return NodeFilter.FILTER_REJECT; + return NodeFilter.FILTER_ACCEPT; + } + }); + var n; + while ((n = walker.nextNode())) { + nodes.push({ node: n, start: text.length, end: text.length + n.data.length }); + text += n.data; + } + return { text: text, nodes: nodes }; + } + function globalOffsetOf(container, offset, index) { + if (container.nodeType === 3) { + for (var i = 0; i < index.nodes.length; i++) { + if (index.nodes[i].node === container) return index.nodes[i].start + offset; + } + } + return -1; + } + function nodeAt(index, globalPos, preferStart) { + // binary search for the text node containing globalPos + var lo = 0, hi = index.nodes.length - 1; + while (lo <= hi) { + var mid = (lo + hi) >> 1; + var e = index.nodes[mid]; + if (globalPos < e.start) hi = mid - 1; + else if (globalPos > e.end || (globalPos === e.end && preferStart && mid + 1 < index.nodes.length)) lo = mid + 1; + else return { node: e.node, offset: globalPos - e.start }; + } + return null; + } + function commonSuffixLen(a, b) { + var n = 0; + while (n < a.length && n < b.length && a[a.length - 1 - n] === b[b.length - 1 - n]) n++; + return n; + } + function findQuote(index, quote, prefix) { + if (!quote) return null; + var hits = []; + var from = 0, at; + while ((at = index.text.indexOf(quote, from)) !== -1) { + hits.push({ start: at, end: at + quote.length }); + from = at + 1; + if (hits.length > 50) break; + } + if (!hits.length) { + // whitespace-tolerant fallback + var pattern = quote.replace(/[.*+?^$()\[\]{}|\\]/g, '\\$&').replace(/\s+/g, '\\s*'); + try { + var m = new RegExp(pattern).exec(index.text); + if (m) hits.push({ start: m.index, end: m.index + m[0].length }); + } catch (e) {} + } + if (!hits.length) return null; + if (hits.length === 1 || !prefix) return hits[0]; + var best = hits[0], bestScore = -1; + hits.forEach(function (h) { + var before = index.text.slice(Math.max(0, h.start - prefix.length), h.start); + var score = commonSuffixLen(before, prefix); + if (score > bestScore) { bestScore = score; best = h; } + }); + return best; + } + function makeRange(index, start, end) { + var s = nodeAt(index, start, true); + var e = nodeAt(index, end, false); + if (!s || !e) return null; + try { + var r = document.createRange(); + r.setStart(s.node, s.offset); + r.setEnd(e.node, e.offset); + return r; + } catch (err) { return null; } + } + + var anchoredRanges = {}; // id -> Range + function anchorAll() { + anchoredRanges = {}; + var list = pageItems().filter(function (it) { return it.type === 'inline'; }); + if (list.length) { + var index = buildTextIndex(); + list.forEach(function (it) { + var hit = findQuote(index, it.quote, it.prefix); + if (hit) { + var r = makeRange(index, hit.start, hit.end); + if (r) anchoredRanges[it.id] = r; + } + }); + } + paintHighlights(); + renderList(); + } + function scheduleAnchor() { + if (anchorTimer) clearTimeout(anchorTimer); + anchorTimer = setTimeout(anchorAll, 400); + } + function paintHighlights() { + if (typeof Highlight === 'undefined' || !CSS.highlights) return; + var ranges = Object.keys(anchoredRanges).map(function (k) { return anchoredRanges[k]; }); + if (ranges.length) { + var hl = new Highlight(); + ranges.forEach(function (r) { hl.add(r); }); + CSS.highlights.set('vast-review', hl); + } else { + CSS.highlights.delete('vast-review'); + } + } + function flashRange(r) { + if (typeof Highlight === 'undefined' || !CSS.highlights) return; + CSS.highlights.set('vast-review-flash', new Highlight(r)); + setTimeout(function () { CSS.highlights.delete('vast-review-flash'); }, 1600); + } + + // ---------------- selection capture ---------------- + var selBtnData = null; + function hideSelBtn() { selBtn.style.display = 'none'; selBtnData = null; } + function onSelectionSettled() { + var sel = document.getSelection(); + if (!sel || sel.isCollapsed || sel.rangeCount === 0) { hideSelBtn(); return; } + var anchor = sel.anchorNode; + if (anchor && host.contains(anchor.nodeType === 1 ? anchor : anchor.parentNode)) { hideSelBtn(); return; } + if (anchor === host) { hideSelBtn(); return; } + var quote = sel.toString().replace(/\s+$/,'').replace(/^\s+/,''); + if (quote.length < 2 || quote.length > 2000) { hideSelBtn(); return; } + var range = sel.getRangeAt(0); + var rect = range.getBoundingClientRect(); + if (!rect || (rect.width === 0 && rect.height === 0)) { hideSelBtn(); return; } + + var index = buildTextIndex(); + var s = globalOffsetOf(range.startContainer, range.startOffset, index); + var e = globalOffsetOf(range.endContainer, range.endOffset, index); + var prefix = '', suffix = ''; + if (s >= 0) prefix = index.text.slice(Math.max(0, s - 40), s); + if (e >= 0) suffix = index.text.slice(e, e + 40); + + selBtnData = { + quote: quote, prefix: prefix, suffix: suffix, + heading: nearestHeading(range) + }; + var x = Math.min(rect.right + 6, window.innerWidth - 130); + var y = Math.min(Math.max(rect.bottom + 6, 10), window.innerHeight - 44); + selBtn.style.left = Math.max(8, x) + 'px'; + selBtn.style.top = y + 'px'; + selBtn.style.display = 'flex'; + } + function nearestHeading(range) { + var hs = document.querySelectorAll('h1, h2, h3, h4'); + var best = ''; + for (var i = 0; i < hs.length; i++) { + var pos = range.startContainer.compareDocumentPosition(hs[i]); + if (pos & Node.DOCUMENT_POSITION_PRECEDING) best = hs[i].textContent.trim(); + } + return best; + } + + // ---------------- item ops ---------------- + var composerCtx = null; // page identity captured when the composer opens, so a + // back/forward navigation mid-typing cannot mislabel the item + function saveItem(fields) { + var it; + if (editingId) { + it = items.filter(function (x) { return x.id === editingId; })[0]; + if (!it) return; + it.category = fields.category; + it.severity = fields.severity; + it.comment = fields.comment; + it.updatedAt = nowIso(); + } else { + it = { + id: uid(), + reviewer: reviewer, + page: composerCtx ? composerCtx.page : location.pathname, + pageTitle: composerCtx ? composerCtx.pageTitle : pageTitle(), + type: pending && pending.quote ? 'inline' : 'page', + quote: pending ? (pending.quote || '') : '', + prefix: pending ? (pending.prefix || '') : '', + suffix: pending ? (pending.suffix || '') : '', + heading: pending ? (pending.heading || '') : '', + category: fields.category, + severity: fields.severity, + comment: fields.comment, + status: 'open', + createdAt: nowIso(), + updatedAt: nowIso() + }; + items.push(it); + } + pending = null; + editingId = null; + scheduleSave(); + scheduleAnchor(); + renderAll(); + toast('Feedback saved'); + } + function deleteItem(id) { + var it = items.filter(function (x) { return x.id === id; })[0]; + if (!it) return; + it.deleted = true; + it.updatedAt = nowIso(); + scheduleSave(); + scheduleAnchor(); + renderAll(); + } + function toggleResolve(id) { + var it = items.filter(function (x) { return x.id === id; })[0]; + if (!it) return; + it.status = it.status === 'resolved' ? 'open' : 'resolved'; + it.updatedAt = nowIso(); + scheduleSave(); + renderAll(); + } + + // ---------------- UI ---------------- + var host = document.createElement('div'); + host.id = '__vast_review_host__'; + var shadow = host.attachShadow({ mode: 'open' }); + + // document-level styles (::highlight can't live in shadow DOM) + var docStyle = document.createElement('style'); + docStyle.textContent = '::highlight(vast-review){background:rgba(255,200,50,.45);}' + + '::highlight(vast-review-flash){background:rgba(74,92,240,.35);}'; + document.head.appendChild(docStyle); + + var css = '' + + ':host{all:initial}' + + '*{box-sizing:border-box;font-family:ui-sans-serif,system-ui,-apple-system,sans-serif}' + + '#pill{position:fixed;right:18px;bottom:18px;z-index:2147483000;display:flex;align-items:center;gap:8px;' + + 'padding:10px 16px;border:none;border-radius:999px;background:#4a5cf0;color:#fff;font-size:14px;font-weight:600;' + + 'cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.25)}' + + '#pill:hover{background:#3a49d6}' + + '#pill .count{background:rgba(255,255,255,.25);border-radius:999px;padding:1px 8px;font-size:12px}' + + '#selbtn{position:fixed;z-index:2147483001;display:none;align-items:center;gap:6px;padding:7px 12px;' + + 'border:none;border-radius:8px;background:#1a1a2e;color:#fff;font-size:13px;font-weight:600;cursor:pointer;' + + 'box-shadow:0 4px 14px rgba(0,0,0,.3)}' + + '#panel{position:fixed;top:0;right:0;bottom:0;width:380px;max-width:95vw;z-index:2147483002;display:none;' + + 'flex-direction:column;background:#fff;color:#1a1a2e;border-left:1px solid #d5d9e4;box-shadow:-6px 0 24px rgba(0,0,0,.12);font-size:13px}' + + '#panel.open{display:flex}' + + '#panel header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;background:#1a1a2e;color:#fff}' + + '#panel header b{font-size:14px}' + + '#panel header button{background:none;border:none;color:#fff;font-size:20px;cursor:pointer;line-height:1}' + + '.meta{display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid #e7eaf1;color:#5c677d}' + + '.meta b{color:#1a1a2e}' + + '.meta button,.filters button{background:#f0f2f8;border:1px solid #d5d9e4;border-radius:6px;padding:3px 9px;cursor:pointer;font-size:12px}' + + '.filters{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 14px;border-bottom:1px solid #e7eaf1}' + + '.filters label{display:flex;align-items:center;gap:6px;cursor:pointer;color:#5c677d}' + + '.filters .primary{background:#4a5cf0;border-color:#4a5cf0;color:#fff;font-weight:600}' + + '#list{flex:1;overflow-y:auto;padding:10px 14px}' + + '.pagegroup{margin:14px 0 6px;font-weight:700;font-size:12px;color:#5c677d;text-transform:uppercase;letter-spacing:.4px}' + + '.card{border:1px solid #e0e4ee;border-radius:10px;padding:10px 12px;margin-bottom:10px;background:#fbfcfe}' + + '.card.resolved{opacity:.55}' + + '.card .chips{display:flex;gap:6px;align-items:center;margin-bottom:6px;flex-wrap:wrap}' + + '.chip{font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;color:#fff}' + + '.chip.cat{background:#5c677d}' + + '.chip.orphan{background:#fff;color:#b58a00;border:1px dashed #b58a00}' + + '.card blockquote{margin:6px 0;padding:4px 10px;border-left:3px solid #ffd166;background:#fffbeb;color:#5c5233;' + + 'font-style:italic;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}' + + '.card .comment{white-space:pre-wrap;margin:6px 0}' + + '.card .byline{color:#8a93a6;font-size:11px;margin-top:4px}' + + '.card .acts{display:flex;gap:10px;margin-top:8px}' + + '.card .acts button{background:none;border:none;padding:0;color:#4a5cf0;font-size:12px;cursor:pointer;font-weight:600}' + + '.card .acts button.danger{color:#d92d20}' + + '.empty{color:#8a93a6;text-align:center;padding:30px 10px}' + + '#panel footer{border-top:1px solid #e7eaf1;padding:10px 14px;display:flex;flex-direction:column;gap:8px;background:#f7f8fc}' + + '.exports{display:flex;gap:8px;align-items:center;flex-wrap:wrap}' + + '.exports a{color:#4a5cf0;font-weight:600;text-decoration:none;font-size:12px;border:1px solid #c9d0f5;border-radius:6px;padding:4px 9px}' + + '#saveStatus{font-size:11px;color:#8a93a6}' + + '#composer,#nameModal{position:fixed;z-index:2147483003;top:50%;left:50%;transform:translate(-50%,-50%);width:420px;max-width:92vw;' + + 'display:none;flex-direction:column;gap:10px;background:#fff;color:#1a1a2e;border-radius:14px;padding:18px;box-shadow:0 12px 48px rgba(0,0,0,.3);font-size:13px}' + + '#composer.open,#nameModal.open{display:flex}' + + '#composer h3,#nameModal h3{margin:0;font-size:15px}' + + '#composer blockquote{margin:0;padding:6px 10px;border-left:3px solid #ffd166;background:#fffbeb;color:#5c5233;font-style:italic;' + + 'max-height:70px;overflow:auto}' + + '.row{display:flex;gap:10px}' + + '.row label{flex:1;display:flex;flex-direction:column;gap:4px;font-weight:600;color:#5c677d;font-size:11px;text-transform:uppercase}' + + 'select,textarea,input[type=text]{border:1px solid #c9cfdd;border-radius:8px;padding:8px;font-size:13px;width:100%;background:#fff;color:#1a1a2e}' + + 'textarea{min-height:90px;resize:vertical}' + + '.btnrow{display:flex;justify-content:flex-end;gap:8px}' + + '.btnrow button{border-radius:8px;padding:8px 16px;font-size:13px;font-weight:600;cursor:pointer;border:1px solid #c9cfdd;background:#fff;color:#1a1a2e}' + + '.btnrow button.primary{background:#4a5cf0;border-color:#4a5cf0;color:#fff}' + + '#overlaybg{position:fixed;inset:0;z-index:2147483002;background:rgba(10,12,30,.35);display:none}' + + '#overlaybg.open{display:block}' + + '#toast{position:fixed;bottom:80px;right:24px;z-index:2147483004;background:#1a1a2e;color:#fff;padding:8px 16px;border-radius:8px;' + + 'font-size:13px;display:none}'; + + var wrap = document.createElement('div'); + wrap.innerHTML = + '' + + '' + + '' + + '
' + + '
' + + '
Docs review — PR 185
' + + '
Reviewer:
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '
' + + '
' + + '

Add feedback

' + + '' + + '
' + + '' + + '' + + '
' + + '' + + '
' + + '
' + + '
' + + '

Who is reviewing?

' + + '

Your name is attached to each comment so feedback can be tracked in Jira.

' + + '' + + '
' + + '
' + + '
'; + shadow.appendChild(wrap); + document.body.appendChild(host); + + function $(id) { return shadow.getElementById(id); } + var pill = $('pill'), selBtn = $('selbtn'), panel = $('panel'), composer = $('composer'), + nameModal = $('nameModal'), overlaybg = $('overlaybg'), toastEl = $('toast'); + + CATEGORIES.forEach(function (c) { + var o = document.createElement('option'); o.value = c; o.textContent = c; $('fCategory').appendChild(o); + }); + SEVERITIES.forEach(function (s) { + var o = document.createElement('option'); o.value = s; o.textContent = s; $('fSeverity').appendChild(o); + }); + $('fCategory').value = 'Suggestion'; + $('fSeverity').value = 'Minor'; + + var toastTimer = null; + function toast(msg) { + toastEl.textContent = msg; + toastEl.style.display = 'block'; + if (toastTimer) clearTimeout(toastTimer); + toastTimer = setTimeout(function () { toastEl.style.display = 'none'; }, 1800); + } + + function renderBadge() { + var n = pageItems().filter(function (i) { return i.status !== 'resolved'; }).length; + var total = visibleItems().length; + $('pillCount').textContent = n + '/' + total; + } + function renderSaveStatus() { + var map = { + idle: '', saving: 'Saving…', + saved: 'Saved to disk ✓', + offline: reviewer ? 'Review server unreachable — stored in browser only' : 'Set your name to save feedback' + }; + $('saveStatus').textContent = map[saveStatus] || ''; + } + function cardHtml(it) { + var eid = esc(it.id); // ids can arrive from shared feedback files — never trust them in markup + var found = !!anchoredRanges[it.id]; + var sev = '' + esc(it.severity) + ''; + var cat = '' + esc(it.category) + ''; + var orphan = (it.type === 'inline' && !found && it.page === location.pathname) + ? 'not found' : ''; + var quote = it.quote ? '
' + esc(it.quote) + '
' : ''; + var headline = it.heading ? '' : ''; + var goBtn = (it.page === location.pathname && found) ? '' : ''; + var openBtn = (it.page !== location.pathname) ? '' : ''; + return '
' + + '
' + sev + cat + orphan + '
' + + quote + headline + + '
' + esc(it.comment) + '
' + + '' + + '
' + goBtn + openBtn + + '' + + '' + + '' + + '
'; + } + function renderList() { + var listEl = $('list'); + var data = showAllPages ? visibleItems() : pageItems(); + if (!data.length) { + listEl.innerHTML = '
No feedback ' + (showAllPages ? 'yet' : 'on this page yet') + + '.

Select any text on the page and click
💬 Comment on selection,
or add a page-level note above.
'; + return; + } + var html = ''; + if (showAllPages) { + var byPage = {}; + data.forEach(function (it) { (byPage[it.page] = byPage[it.page] || []).push(it); }); + Object.keys(byPage).sort().forEach(function (pg) { + html += '
' + esc(pg) + '
'; + byPage[pg].forEach(function (it) { html += cardHtml(it); }); + }); + } else { + data.forEach(function (it) { html += cardHtml(it); }); + } + listEl.innerHTML = html; + } + function renderWho() { $('who').textContent = reviewer || '—'; } + function renderAll() { renderBadge(); renderList(); renderWho(); renderSaveStatus(); } + + function openPanel() { panel.classList.add('open'); renderAll(); } + function closePanel() { panel.classList.remove('open'); } + function openComposer(title, quote) { + if (!reviewer) { pendingAfterName = function () { openComposer(title, quote); }; openNameModal(); return; } + composerCtx = { page: location.pathname, pageTitle: pageTitle() }; + $('composerTitle').textContent = title; + var q = $('composerQuote'); + if (quote) { q.textContent = quote; q.style.display = 'block'; } else { q.style.display = 'none'; } + composer.classList.add('open'); + overlaybg.classList.add('open'); + $('fComment').focus(); + } + function closeComposer() { + composer.classList.remove('open'); + overlaybg.classList.remove('open'); + $('fComment').value = ''; + pending = null; + editingId = null; + } + var pendingAfterName = null; + function openNameModal() { + nameModal.classList.add('open'); + overlaybg.classList.add('open'); + $('fName').value = reviewer; + $('fName').focus(); + } + function closeNameModal() { + nameModal.classList.remove('open'); + if (!composer.classList.contains('open')) overlaybg.classList.remove('open'); + } + + // ---------------- events ---------------- + pill.addEventListener('click', function () { + if (panel.classList.contains('open')) closePanel(); else openPanel(); + }); + $('closePanel').addEventListener('click', closePanel); + $('editWho').addEventListener('click', openNameModal); + $('allPages').addEventListener('change', function (e) { showAllPages = e.target.checked; renderList(); }); + $('addPageNote').addEventListener('click', function () { + pending = null; editingId = null; + openComposer('Page note — ' + location.pathname, ''); + }); + selBtn.addEventListener('click', function () { + pending = selBtnData; + hideSelBtn(); + var sel = document.getSelection(); + if (sel) sel.removeAllRanges(); + editingId = null; + openComposer('Comment on selection', pending ? pending.quote : ''); + }); + $('composerCancel').addEventListener('click', closeComposer); + $('composerSave').addEventListener('click', function () { + var comment = $('fComment').value.trim(); + if (!comment) { $('fComment').focus(); return; } + saveItem({ category: $('fCategory').value, severity: $('fSeverity').value, comment: comment }); + closeComposer(); + }); + $('nameSave').addEventListener('click', function () { + var v = $('fName').value.trim(); + if (!v) { $('fName').focus(); return; } + reviewer = v; + try { localStorage.setItem(LS_REVIEWER, reviewer); } catch (e) {} + closeNameModal(); + renderWho(); + mergeServerState(); + if (pendingAfterName) { var f = pendingAfterName; pendingAfterName = null; f(); } + }); + $('fName').addEventListener('keydown', function (e) { if (e.key === 'Enter') $('nameSave').click(); }); + overlaybg.addEventListener('click', function () { closeComposer(); closeNameModal(); }); + $('list').addEventListener('click', function (e) { + var btn = e.target.closest('button[data-act]'); + if (!btn) return; + var id = btn.getAttribute('data-id'); + var act = btn.getAttribute('data-act'); + var it = items.filter(function (x) { return x.id === id; })[0]; + if (!it) return; + if (act === 'delete') { if (confirm('Delete this feedback item?')) deleteItem(id); } + else if (act === 'resolve') toggleResolve(id); + else if (act === 'open') { + // only same-origin paths — pages can arrive from shared feedback files + if (/^\/([^\/]|$)/.test(String(it.page || ''))) location.href = it.page; + } + else if (act === 'go') { + var r = anchoredRanges[id]; + if (r) { + var el = r.startContainer.nodeType === 1 ? r.startContainer : r.startContainer.parentElement; + if (el && el.scrollIntoView) el.scrollIntoView({ block: 'center', behavior: 'smooth' }); + flashRange(r); + } + } + else if (act === 'edit') { + editingId = id; + pending = null; + $('fCategory').value = it.category || CATEGORIES[0]; + $('fSeverity').value = it.severity || 'Minor'; + openComposer('Edit feedback', it.quote || ''); + $('fComment').value = it.comment || ''; + } + }); + document.addEventListener('keydown', function (e) { + if (e.key === 'Escape') { closeComposer(); closeNameModal(); hideSelBtn(); } + }); + document.addEventListener('pointerup', function (e) { + var path = e.composedPath ? e.composedPath() : []; + if (path.indexOf(host) !== -1) return; + setTimeout(onSelectionSettled, 30); + }); + document.addEventListener('keyup', function (e) { + if (e.shiftKey || e.key === 'Shift') setTimeout(onSelectionSettled, 30); + }); + // click on a highlight opens the panel scrolled to that card + document.addEventListener('click', function (e) { + var path = e.composedPath ? e.composedPath() : []; + if (path.indexOf(host) !== -1) return; + var ids = Object.keys(anchoredRanges); + for (var i = 0; i < ids.length; i++) { + var rects = anchoredRanges[ids[i]].getClientRects(); + for (var j = 0; j < rects.length; j++) { + var rc = rects[j]; + if (e.clientX >= rc.left && e.clientX <= rc.right && e.clientY >= rc.top && e.clientY <= rc.bottom) { + openPanel(); + var card = shadow.querySelector('[data-card="' + CSS.escape(ids[i]) + '"]'); + if (card) { card.scrollIntoView({ block: 'center' }); card.style.outline = '2px solid #4a5cf0'; } + return; + } + } + } + }, true); + + // SPA navigation: re-anchor highlights when the route or DOM changes + function onNavigate() { + hideSelBtn(); + scheduleAnchor(); + setTimeout(renderAll, 450); + } + var origPush = history.pushState; + history.pushState = function () { origPush.apply(this, arguments); onNavigate(); }; + var origReplace = history.replaceState; + history.replaceState = function () { origReplace.apply(this, arguments); onNavigate(); }; + window.addEventListener('popstate', onNavigate); + new MutationObserver(function (muts) { + for (var i = 0; i < muts.length; i++) { + if (muts[i].target === host || host.contains(muts[i].target)) continue; + scheduleAnchor(); + return; + } + }).observe(document.body, { childList: true, subtree: true }); + + // ---------------- boot ---------------- + renderAll(); + scheduleAnchor(); + if (reviewer) { mergeServerState(); saveStatus = 'saved'; } + renderSaveStatus(); +})(); +`; + +// ------------------------------------------------------------------ proxy +const INJECT_TAG = ''; + +function injectOverlay(html) { + const lower = html.toLowerCase(); + let at = lower.lastIndexOf(''); + if (at === -1) at = lower.lastIndexOf(''); + if (at === -1) return html + INJECT_TAG; + return html.slice(0, at) + INJECT_TAG + html.slice(at); +} + +function readBody(req, limitBytes) { + return new Promise((resolve, reject) => { + const chunks = []; + let size = 0; + req.on('data', (c) => { + size += c.length; + if (size > limitBytes) { reject(new Error('body too large')); req.destroy(); return; } + chunks.push(c); + }); + req.on('end', () => resolve(Buffer.concat(chunks))); + req.on('error', reject); + }); +} + +function sendJson(res, code, obj) { + const body = JSON.stringify(obj); + res.writeHead(code, { 'content-type': 'application/json', 'cache-control': 'no-store' }); + res.end(body); +} + +async function handleReviewRoute(req, res, url) { + const p = url.pathname; + if (p === '/__review__/' || p === '/__review__') { + res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' }); + res.end(statusPage()); + return; + } + if (p === '/__review__/overlay.js') { + res.writeHead(200, { 'content-type': 'application/javascript; charset=utf-8', 'cache-control': 'no-store' }); + res.end(OVERLAY_JS); + return; + } + if (p === '/__review__/api/state' && req.method === 'GET') { + const reviewer = url.searchParams.get('reviewer') || ''; + sendJson(res, 200, { reviewer, items: readReviewerState(reviewer) }); + return; + } + if (p === '/__review__/api/state' && req.method === 'POST') { + try { + const body = JSON.parse((await readBody(req, 8 * 1024 * 1024)).toString('utf8')); + if (!body || typeof body.reviewer !== 'string' || !body.reviewer.trim() || !Array.isArray(body.items)) { + sendJson(res, 400, { error: 'expected { reviewer, items[] }' }); + return; + } + writeReviewerState(body.reviewer.trim(), body.items); + sendJson(res, 200, { ok: true, saved: body.items.length }); + } catch (e) { + sendJson(res, 400, { error: String(e.message || e) }); + } + return; + } + if (p.startsWith('/__review__/export/')) { + const { items, reviewers } = readAllItems(); + const kind = p.split('/').pop(); + if (kind === 'feedback.csv') { + res.writeHead(200, { + 'content-type': 'text/csv; charset=utf-8', + 'content-disposition': 'attachment; filename="pr185-docs-feedback.csv"', + 'cache-control': 'no-store', + }); + res.end(toCsv(items)); + return; + } + if (kind === 'feedback.md') { + res.writeHead(200, { + 'content-type': 'text/markdown; charset=utf-8', + 'content-disposition': 'attachment; filename="pr185-docs-feedback.md"', + 'cache-control': 'no-store', + }); + res.end(toMarkdown(items, reviewers)); + return; + } + if (kind === 'feedback.json') { + res.writeHead(200, { + 'content-type': 'application/json', + 'content-disposition': 'attachment; filename="pr185-docs-feedback.json"', + 'cache-control': 'no-store', + }); + res.end(JSON.stringify({ generatedAt: new Date().toISOString(), pr: PR_URL, reviewers, items }, null, 2)); + return; + } + } + res.writeHead(404, { 'content-type': 'text/plain' }); + res.end('not found'); +} + +function badGateway(res, err) { + if (res.headersSent) { try { res.destroy(); } catch { /* already gone */ } return; } + res.writeHead(502, { 'content-type': 'text/html; charset=utf-8' }); + res.end(` +

Docs preview is not running

+

The review server could not reach the Mintlify preview at ${esc(TARGET.origin)}.

+

In the repo folder, start it first:

+
npm run dev -- --no-open
+

then reload this page. If the preview started on a different port, restart this server with +node review-server.mjs --target http://localhost:<port>.

+

(${esc(err.message || err)})

`); +} + +const server = http.createServer(async (req, res) => { + const url = new URL(req.url, `http://localhost:${PORT}`); + if (url.pathname.startsWith('/__review__')) { + try { await handleReviewRoute(req, res, url); } catch (e) { sendJson(res, 500, { error: String(e.message || e) }); } + return; + } + + const headers = { ...req.headers, host: TARGET.host, 'accept-encoding': 'identity' }; + const upstream = http.request( + { hostname: TARGET.hostname, port: TARGET.port || 80, path: req.url, method: req.method, headers }, + (up) => { + const outHeaders = { ...up.headers }; + delete outHeaders['content-security-policy']; + delete outHeaders['content-security-policy-report-only']; + if (outHeaders.location && outHeaders.location.startsWith(TARGET.origin)) { + outHeaders.location = outHeaders.location.slice(TARGET.origin.length) || '/'; + } + const ctype = String(up.headers['content-type'] || ''); + if (ctype.includes('text/html') && req.method !== 'HEAD') { + const chunks = []; + up.on('data', (c) => chunks.push(c)); + up.on('end', () => { + const html = injectOverlay(Buffer.concat(chunks).toString('utf8')); + delete outHeaders['content-length']; + delete outHeaders['transfer-encoding']; + outHeaders['content-length'] = Buffer.byteLength(html); + res.writeHead(up.statusCode, outHeaders); + res.end(html); + }); + up.on('error', () => { try { res.destroy(); } catch {} }); + } else { + res.writeHead(up.statusCode, outHeaders); + up.pipe(res); + up.on('error', () => { try { res.destroy(); } catch { /* already gone */ } }); + } + } + ); + upstream.on('error', (err) => badGateway(res, err)); + req.pipe(upstream); +}); + +// websocket passthrough (Next.js HMR etc.) +server.on('upgrade', (req, socket, head) => { + const upstream = net.connect(Number(TARGET.port || 80), TARGET.hostname, () => { + let raw = `${req.method} ${req.url} HTTP/1.1\r\n`; + for (let i = 0; i < req.rawHeaders.length; i += 2) { + const k = req.rawHeaders[i]; + const v = k.toLowerCase() === 'host' ? TARGET.host : req.rawHeaders[i + 1]; + raw += `${k}: ${v}\r\n`; + } + raw += '\r\n'; + upstream.write(raw); + if (head && head.length) upstream.write(head); + socket.pipe(upstream); + upstream.pipe(socket); + }); + upstream.on('error', () => socket.destroy()); + socket.on('error', () => upstream.destroy()); +}); + +server.listen(PORT, () => { + console.log(''); + console.log(' Vast.ai docs review server (PR #185)'); + console.log(' ------------------------------------'); + console.log(` Review the docs at: http://localhost:${PORT}/host/hosting-overview`); + console.log(` Proxying preview at: ${TARGET.origin} (start it with: npm run dev -- --no-open)`); + console.log(` Feedback saved to: ${FEEDBACK_DIR}`); + console.log(` Status & exports: http://localhost:${PORT}/__review__/`); + console.log(''); +}); From 86bfa162d14c51b608f1c75056fa341c2ff972c8 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Fri, 10 Jul 2026 12:04:20 +0200 Subject: [PATCH 16/26] CON-1518 close IA audit gaps: anchors, canonical thresholds, journey seams - self-test-reference: anchors for every quick-table error family (reliability, auth-error, direct-ports, bandwidth, insufficient-resources, system-requirements) so CLI errors and support replies can deep-link - how-to-self-test: fix 7 GiB -> 7 GB drift (CLI gate is decimal GB, vast.py:9549) and defer to verification-stages as the canonical gate list - machine-errors: mark self-test-reference as canonical for nccl_failed and machine-not-rentable to remove duplicate retrieval targets - installing-host-software: anchored identify-4xx section with the concrete root causes (fresh-key guidance per Hanran's 2026-06-16 corrections) - hosting-overview <-> quickstart now cross-linked; quickstart gains the Earn step; account page sidebar renamed Account & Agreement to stop colliding with Reference's Hosting Agreement - merge main: slot host/notifications into Operate with persona chips; reorder Verify & List so optimization-guide follows listing tasks Co-Authored-By: Claude Fable 5 --- host/account-hosting-agreement.mdx | 2 +- host/fleet-operations.mdx | 1 + host/hosting-overview.mdx | 4 +++- host/how-to-self-test.mdx | 4 +++- host/installing-host-software.mdx | 12 ++++++++++++ host/machine-errors.mdx | 4 ++-- host/quickstart.mdx | 1 + host/self-test-reference.mdx | 26 ++++++++++++++++++++++++++ 8 files changed, 49 insertions(+), 5 deletions(-) diff --git a/host/account-hosting-agreement.mdx b/host/account-hosting-agreement.mdx index 391f939b..030384ac 100644 --- a/host/account-hosting-agreement.mdx +++ b/host/account-hosting-agreement.mdx @@ -1,6 +1,6 @@ --- title: "Host Account and Agreement" -sidebarTitle: "Host Agreement" +sidebarTitle: "Account & Agreement" description: "Host account conversion and agreement flow." "canonical": "/host/account-hosting-agreement" personas: diff --git a/host/fleet-operations.mdx b/host/fleet-operations.mdx index d07d8e10..58c92ca7 100644 --- a/host/fleet-operations.mdx +++ b/host/fleet-operations.mdx @@ -96,6 +96,7 @@ To decommission, unlist first, honor remaining rental end dates, then delete. Se | Topic | Read next | | --- | --- | | Headless install | [Headless Install](/host/headless-install) | +| Datacenter verification | [Datacenter Status](/host/datacenter-status) | | Host CLI/API/SDK | [Host CLI/API/SDK](/host/cli-api-sdk) | | Maintenance | [Maintenance Windows](/host/maintenance-windows) | | Pricing | [Pricing Your Listing](/host/pricing-your-listing) | diff --git a/host/hosting-overview.mdx b/host/hosting-overview.mdx index 21127800..1ee79a60 100644 --- a/host/hosting-overview.mdx +++ b/host/hosting-overview.mdx @@ -32,7 +32,9 @@ After signup, use this docs path for hardware, storage, networking, install, and Before using API keys, CLI commands, SDK automation, or team access for hosting, review [Host Account Security](/host/account-security-for-hosts). -For setup, follow: +New to hosting? The [Hosting Quickstart](/host/quickstart) walks this same path as one numbered journey. Not sure hosting fits your hardware or expectations? Start with [Is Vast for Me?](/host/persona-decision-guide). + +For direct access to each setup topic: 1. [Supported Hardware](/host/supported-hardware) 2. [Hardware Prep](/host/hardware-prep) diff --git a/host/how-to-self-test.mdx b/host/how-to-self-test.mdx index c6207d2f..4965b8cb 100644 --- a/host/how-to-self-test.mdx +++ b/host/how-to-self-test.mdx @@ -43,9 +43,11 @@ Before renting the temporary diagnostic instance, the CLI checks whether the mac - Direct ports are at least 3 ports per listed GPU. - PCIe bandwidth is greater than 2.85 GB/s. - Upload/download bandwidth meet the VRAM-scaled requirement. -- GPU VRAM is greater than 7 GiB per GPU. +- GPU VRAM is greater than 7 GB per GPU. - System RAM is close to total GPU VRAM, capped around 2 TB for very high-VRAM machines. +These are the same gates as the platform verification requirements. The canonical list lives in [Verification Stages: requirements](/host/verification-stages#additional-requirements-for-verification); if the numbers here ever differ, that page wins. + Inside the temporary self-test image, checks include: - HTTPS progress endpoint reachability. diff --git a/host/installing-host-software.mdx b/host/installing-host-software.mdx index be1ffef0..84b89707 100644 --- a/host/installing-host-software.mdx +++ b/host/installing-host-software.mdx @@ -102,6 +102,18 @@ Common causes: Refresh the setup page, copy a fresh command, confirm host account access, and rerun the wizard. Use the wizard error, installer log, and daemon logs to decide what to fix. + +### Daemon identify or API 4xx errors + +If the installer or daemon logs show an HTTP 4xx while registering or identifying the machine, work through these causes in order: + +1. **Truncated or mangled setup key.** Always copy the full command with the setup page's copy button; a partial paste fails authentication. Get a fresh command and rerun. +2. **Expired setup key.** Setup commands are single-use and expire. Refresh the setup page and copy a fresh command — do not rerun an old one from shell history. +3. **Account not converted to a host account.** The API rejects machines from client-only accounts. See [Host Account and Agreement](/host/account-hosting-agreement#agreement-stuck). +4. **Stale installer or retired endpoint.** An old downloaded installer can call endpoints that no longer exist. Re-download the installer from the setup page instead of reusing a cached copy. + +If the log shows a local driver, Docker, or cgroup error rather than an API 4xx, use the general causes above and [Host Diagnostics](/host/common-errors-diagnostics#logs). + ## Logs Check `vast_host_install.log`, the `vastai.service` systemd unit, and the kaalia logs. For log locations and diagnostic bundles, see [Host Diagnostics](/host/common-errors-diagnostics#logs). diff --git a/host/machine-errors.mdx b/host/machine-errors.mdx index 6826e585..ac2cdc10 100644 --- a/host/machine-errors.mdx +++ b/host/machine-errors.mdx @@ -220,7 +220,7 @@ nvidia-smi -q | grep -i -A 2 Fabric nvidia-smi topo -m ``` -See [Self-Test Reference: nccl_failed](/host/self-test-reference#nccl-failed). +Canonical entry (including NVSwitch/Fabric Manager checks): [Self-Test Reference: nccl_failed](/host/self-test-reference#nccl-failed). ## XFS Project Quota Error @@ -253,7 +253,7 @@ Do not manually delete renter data. For expired or deleted rentals that did not Start in the Machines page for the host account. Confirm the machine is listed, online, not currently rented in a way that hides the expected offer, has active offers, and shows required details such as ports, RAM, upload speed, and download speed. Then confirm the CLI is authenticated to the same host-enabled account. -See [Self-Test Reference: machine not rentable](/host/self-test-reference#machine-not-rentable) and [Not in Search](/host/not-in-search). +Canonical entry: [Self-Test Reference: machine not rentable](/host/self-test-reference#machine-not-rentable). See also [Not in Search](/host/not-in-search). ## Generic Red Machine Error diff --git a/host/quickstart.mdx b/host/quickstart.mdx index fb0483c4..812c4c3e 100644 --- a/host/quickstart.mdx +++ b/host/quickstart.mdx @@ -20,3 +20,4 @@ Follow this ordered path for a first-time Vast host setup. Each step links to th 5. **List the machine.** Set your price and terms with [Pricing Your Listing](/host/pricing-your-listing). 6. **Run the self-test.** Follow [How to Self-Test](/host/how-to-self-test), and decode any failures with the [Self-Test Reference](/host/self-test-reference). 7. **Understand verification and first-rental expectations.** Read [Understanding Verification](/host/understanding-verification) and [First 24 Hours After Install](/host/first-24-hours). +8. **Start earning.** Understand how rentals turn into income with the [Earnings Model](/host/earning), and set up payouts with [Host Payouts](/host/payment). diff --git a/host/self-test-reference.mdx b/host/self-test-reference.mdx index ff6e01f3..24d619ad 100644 --- a/host/self-test-reference.mdx +++ b/host/self-test-reference.mdx @@ -97,10 +97,36 @@ Fabric state should normally show completed/successful registration on systems w Avoid ignoring warnings by default. One clear self-test advisory is that mapping more than the minimum number of direct ports per GPU is not itself a self-test gate. For production hosting, about 100 forwarded TCP/UDP ports per listed GPU is still practical headroom because individual instances can use many ports and old mappings may overlap with new starts. Red machine errors, self-test failures, NVML/Xid errors, port networking issues, and not-visible/rentable states should be investigated. + ## Reliability below the preflight gate Reliability greater than 0.9 is a self-test and verification gate; if the machine is at or below it, the self-test can fail before renting the temporary instance. See [Reliability & Uptime](/host/reliability-uptime#reliability-threshold). + +## Authorization Error or HTTP 401 + +The API key running the self-test cannot inspect the machine or its offer. Confirm the CLI is authenticated to the host-enabled account that owns the machine, and that the API key has machine and offer permissions. For key handling, see [Host Account Security](/host/account-security-for-hosts). + + +## Direct ports below requirement + +The machine does not expose enough direct ports for its listed GPUs. The self-test gate is at least 3 forwarded TCP and UDP ports per listed GPU; production hosting should use far more headroom. See [Network & Ports](/host/network-ports#ports-per-gpu) for the port formula and forwarding checklist. + + +## Upload or download bandwidth failure + +Measured network throughput is below the machine's requirement, which scales with total GPU VRAM. Check ISP or datacenter bandwidth, routing, and host load, and compare repeated speed results. The canonical bandwidth requirement lives in [Verification Stages: requirements](/host/verification-stages#additional-requirements-for-verification). + + +## VRAM, RAM, or CPU core failure + +Hardware resources do not meet the listed GPU requirement: more than 7 GB VRAM per GPU, system RAM close to total GPU VRAM, and at least one physical CPU core per visible GPU. Confirm all GPUs are visible with `nvidia-smi` and compare host RAM and core counts against the gates in [Verification Stages: requirements](/host/verification-stages#additional-requirements-for-verification). + + +## System requirements failure + +The temporary test container could not confirm core host resources. Check available RAM, CPU cores, and GPU visibility from inside a container, and read the container logs in the diagnostic bundle; see [Host Diagnostics](/host/common-errors-diagnostics#logs). + ## Errors not specific to the self-test For `bad bandwidthtest2`, CDI device injection failures, NVML mismatches, and other machine-health errors, see [Machine Error Reference](/host/machine-errors) and [Host Diagnostics](/host/common-errors-diagnostics). From bc5361b320c338a52e1c432d111c8a49a07a8305 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Fri, 10 Jul 2026 12:10:55 +0200 Subject: [PATCH 17/26] CON-1518 route legacy alias anchors and link CLI self-test to error reference - common-errors-diagnostics: the six stacked legacy anchors now land on a routing list that jumps to the canonical machine-errors entries - CLI self-test snippet: link failures to Self-Test Reference / Machine Error Reference, closing the CLI-docs side of the deep-link loop Co-Authored-By: Claude Fable 5 --- host/common-errors-diagnostics.mdx | 7 +++++++ snippets/host/cli/self-test-machine.mdx | 2 ++ 2 files changed, 9 insertions(+) diff --git a/host/common-errors-diagnostics.mdx b/host/common-errors-diagnostics.mdx index d56b7881..72109077 100644 --- a/host/common-errors-diagnostics.mdx +++ b/host/common-errors-diagnostics.mdx @@ -99,6 +99,13 @@ Services should be `active`, logs should not show restart loops or repeated fata Use these commands to collect GPU, PCIe, AER, Xid, driver, and Docker GPU-injection evidence after an exact error has pointed you toward GPU or runtime health. For error meanings and first-response guidance, use [Machine Error Reference](/host/machine-errors). +Coming from an older error deep link? Jump straight to the canonical entry: + +- `bad bandwidthtest2` → [Machine Errors: Bad Bandwidthtest2](/host/machine-errors#bad-bandwidthtest2) +- PCIe, AER, or Xid issues → [Machine Errors: GPU PCIe Issue](/host/machine-errors#gpu-pcie-issue) +- CDI device injection failures → [Machine Errors: CDI Device Injection](/host/machine-errors#cdi-device-injection) +- `nvidia-smi` failures, NVML mismatch, or a GPU falling off the bus → [Machine Errors: GPU Missing Or Unhealthy](/host/machine-errors#gpu-missing-or-unhealthy) + - **NVRM**: NVIDIA kernel driver messages. - **Xid**: NVIDIA GPU fault, reset, or error codes. - **PCIe**: the bus/link between the GPU, motherboard, and CPU. diff --git a/snippets/host/cli/self-test-machine.mdx b/snippets/host/cli/self-test-machine.mdx index 98259301..0eda832c 100644 --- a/snippets/host/cli/self-test-machine.mdx +++ b/snippets/host/cli/self-test-machine.mdx @@ -41,6 +41,8 @@ vastai self-test machine [--debugging] [--ignore-requirements] [--t This command tests if a machine meets specific requirements and runs a series of tests to ensure it's functioning correctly. +If the self-test fails, decode the exact message with the [Self-Test Reference](/host/self-test-reference). For machine-level errors surfaced outside the self-test, see the [Machine Error Reference](/host/machine-errors). + ## Examples ```bash From 6f04d2154f144608c480fa181c08b9e18a7d0125 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Fri, 10 Jul 2026 12:14:15 +0200 Subject: [PATCH 18/26] CON-1518 add persona chip sync check scripts/check_persona_chips.py verifies that personas: frontmatter and the visible persona-chips div stay in sync on every authored host page (slug->label mapping, all-four collapse to 'All host personas', order-free membership; generated cli/sdk pages exempt). Run with: npm run check-persona-chips Closes the last accepted-risk item from the CON-1518 IA audit. Co-Authored-By: Claude Fable 5 --- package.json | 1 + scripts/check_persona_chips.py | 93 ++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 scripts/check_persona_chips.py diff --git a/package.json b/package.json index 3973d954..a678ac5f 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "scripts": { "build-openapi": "python3 api-reference/openapi/build.py", "check-openapi": "mint openapi-check api-reference/openapi.yaml", + "check-persona-chips": "python3 scripts/check_persona_chips.py", "dev": "mint dev" }, "dependencies": { diff --git a/scripts/check_persona_chips.py b/scripts/check_persona_chips.py new file mode 100644 index 00000000..0c23b48a --- /dev/null +++ b/scripts/check_persona_chips.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +"""Check that persona frontmatter and visible persona chips stay in sync. + +Every authored host page (host/*.mdx) carries the persona taxonomy twice: +machine-readable `personas:` frontmatter and a hand-written chip div rendered +top-right of the page. This check fails when the two drift apart. + +Conventions enforced (matching the CON-1518 IA rulings): + - slug -> chip label: pro-operator "Pro Operator", headless-operator + "Headless / DC", business-owner "Business", hobbyist "Hobbyist" + - all four personas collapse to a single "All host personas" chip + - chip order within the div is free; membership must match exactly + - generated host/cli/* and host/sdk/* pages are exempt (untagged by design) + +Usage: python3 scripts/check_persona_chips.py (exit 0 = in sync) +""" + +import glob +import re +import sys + +SLUG_TO_LABEL = { + "pro-operator": "Pro Operator", + "headless-operator": "Headless / DC", + "business-owner": "Business", + "hobbyist": "Hobbyist", +} +ALL_PERSONAS_LABEL = "All host personas" + +FRONTMATTER_RE = re.compile(r"^personas:\n((?:[ \t]+- .+\n)+)", re.M) +SLUG_RE = re.compile(r"[ \t]+- (.+)") +CHIP_DIV_RE = re.compile(r'
(.*?)
', re.S) +CHIP_RE = re.compile(r'([^<]+)') + + +def check_file(path): + errors = [] + src = open(path, encoding="utf-8").read() + + fm = FRONTMATTER_RE.search(src) + slugs = [s.strip() for s in SLUG_RE.findall(fm.group(1))] if fm else [] + divs = CHIP_DIV_RE.findall(src) + + if not slugs: + errors.append("missing or empty `personas:` frontmatter") + if not divs: + errors.append("missing persona-chips div") + if len(divs) > 1: + errors.append(f"expected 1 persona-chips div, found {len(divs)}") + if not slugs or not divs: + return errors + + unknown = [s for s in slugs if s not in SLUG_TO_LABEL] + if unknown: + errors.append(f"unknown persona slug(s) in frontmatter: {unknown}") + return errors + if len(set(slugs)) != len(slugs): + errors.append(f"duplicate persona slug(s) in frontmatter: {slugs}") + + chips = CHIP_RE.findall(divs[0]) + if set(slugs) == set(SLUG_TO_LABEL): + expected = [ALL_PERSONAS_LABEL] + else: + expected = [SLUG_TO_LABEL[s] for s in slugs] + + if sorted(chips) != sorted(expected): + errors.append( + f"chips {chips} do not match personas frontmatter " + f"(expected {sorted(expected)} in any order)" + ) + return errors + + +def main(): + pages = sorted(glob.glob("host/*.mdx")) + if not pages: + print("check_persona_chips: no host/*.mdx pages found — run from the repo root") + return 1 + + failed = 0 + for path in pages: + for err in check_file(path): + print(f"FAIL {path}: {err}") + failed += 1 + if failed: + print(f"\ncheck_persona_chips: {failed} problem(s) across {len(pages)} pages") + return 1 + print(f"check_persona_chips: OK — {len(pages)} pages, frontmatter and chips in sync") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From cf88d21857282d7e7d07245f3ff51c31d59bad07 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Fri, 10 Jul 2026 13:42:55 +0200 Subject: [PATCH 19/26] CON-1187 surface the 8 blocking reviewer inputs on the PR REVIEW-QUESTIONS.md (GitHub-linkable, commentable in the PR diff) and a hidden /review-questions preview page (annotatable via the review kit, answers land in the same feedback export). Both temporary review aids, removed before merge. Reviewers previously had no pointer from the roadmap's '8 inputs' to the actual questions. Co-Authored-By: Claude Fable 5 --- REVIEW-QUESTIONS.md | 100 +++++++++++++++++++++++++++++++++++++++++++ review-questions.mdx | 83 +++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 REVIEW-QUESTIONS.md create mode 100644 review-questions.mdx diff --git a/REVIEW-QUESTIONS.md b/REVIEW-QUESTIONS.md new file mode 100644 index 00000000..7541804c --- /dev/null +++ b/REVIEW-QUESTIONS.md @@ -0,0 +1,100 @@ +# The 8 blocking inputs — what we need from reviewers + +> Temporary review aid — removed before merge, like `review-server.mjs`. +> +> These are the eight decisions/confirmations that finish the Host Docs epic +> (CON-1187). The writing is done; every item below is a **decision, review, or +> confirmation from your side** — nothing here is writing work. The first two +> unblock everything else. +> +> **Three ways to answer — pick whichever is easiest:** +> 1. Comment inline on this file in the [PR #185 diff](https://github.com/vast-ai/docs/pull/185/files). +> 2. In the local review kit, open `http://localhost:4000/review-questions`, select a question, and comment — it lands in the same feedback export as your page comments. +> 3. Reply on the linked Jira ticket. + +## Input 1. IA approval + +**Owner: Michele + docs owners · Round 0 · unblocks everything** + +Approve or modify the information architecture (CON-1518 decision points a–d): + +- (a) **Lifecycle sidebar** — Before You Host → Set Up → Verify & List → Operate → Reference, vs. some other top-level grouping. +- (b) **P0/P1/P2 priority order** — the ticket-volume-driven ordering of new docs. +- (c) **Supported Hardware as the #1 prevention doc** — even though it isn't the largest ticket bucket. +- (d) **Persona tags** — keep the visible chips (top-right of each page), restyle them, or drop to frontmatter-only convention. + +Blocks: every other review round. Detail: CON-1518. + +## Input 2. Review mechanics + +**Owner: Michele + docs owners** + +How do you want to review the content: one small PR per sidebar group (round), or staged review of [PR #185](https://github.com/vast-ai/docs/pull/185) with per-round checklists? PR #185 now contains all the work (former #153 is an ancestor of it). Either way, **#152 and #153 should be closed as superseded**. + +Blocks: scheduling of every content round. Detail: CON-1518, CON-1584. + +## Input 3. Pricing content review + +**Owner: Gobind / Solutions Engineering · Round 3 · CON-1256** + +Review the business/pricing positioning: [Pricing Your Listing](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/pricing-your-listing.mdx), [Market Metrics](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/market-metrics.mdx), [Optimize Your Earnings](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/optimization-guide.mdx) — in the preview: `/host/pricing-your-listing`, `/host/market-metrics`, `/host/optimization-guide`. + +Blocks: CON-1256 sign-off. Detail: CON-1256. + +## Input 4. Machine-error platform behavior + +**Owner: Backend source owner (Hanran) · CON-1531** + +Seven confirmations that set the "how long it persists" copy on [Machine Error Reference](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/machine-errors.mdx) (`/host/machine-errors` in the preview): + +1. Is the 2026-06-24 error catalog complete for host-visible machine errors? +2. For each error, which field displays it to hosts (`error_msg`, `error_note`, `error_description`, `vm_error_msg`, `vm_error_level`, other)? +3. Which errors appear on the Machines page vs. only in a failed rental/instance detail? +4. For machine-deverifying errors, what clears the error — next clean heartbeat, successful self-test, admin action, time decay? +5. For VM-offer-only errors, what is the approximate clean-report/TTL before VM offers return? +6. Should logged-only rental-attempt messages be public host docs, or internal/support-only? +7. Should the console deep-link docs by raw error string, normalized category, or both? + +Blocks: final wording on Machine Error Reference. Detail: CON-1531. + +## Input 5. Installer Wizard screenshot + +**Owner: Product · Rounds 0/2** + +Approve the Host Installer Wizard (TUI) screenshot in [Installing Host Software](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/installing-host-software.mdx) (`/host/installing-host-software#host-installer-wizard` in the preview) — or supply a replacement asset. + +Blocks: production merge (flagged since 2026-06-17). Detail: CON-1518 Jira attachment `image-20260617-135801.png`. + +## Input 6. Supported Hardware sign-off + +**Owner: Product · Round 1** + +Confirm [Supported Hardware](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/supported-hardware.mdx) (`/host/supported-hardware` in the preview): exact GPU-family coverage, OS/cgroup guidance, and alignment of the CPU rule between docs, self-test #6, and vast-cli #413. Related product asks on the radar: payment/tax edge cases (incl. W-8 for non-US hosts) and datacenter requirements wording. + +Blocks: CON-1516 sign-off; the highest-prevention doc going live. Detail: CON-1516. + +## Input 7. Host Teams engineering answers + +**Owner: Engineering · Round 6 · CON-1581** + +Five answers that gate publishing [Host Teams](https://github.com/jjziets/docs/blob/CON-1584-host-cli-api-sdk/host/host-teams.mdx) (`/host/host-teams` in the preview): + +1. Individual→team migration: what happens to existing machines and accrued earnings? +2. Install-command `undefined` bug in team context — status? +3. What is the exact flow for granting machine-registration rights to a team API key? (Flagged inside the draft page itself.) +4. Are `billing_read`-only roles viable for host teams? +5. Who owns billing/payouts when machines move into a team? + +Blocks: Host Teams page publication. Detail: CON-1581. + +## Input 8. Persona scope ruling + +**Owner: Docs team · Round 5** + +Do the generated `host/cli/*` and `host/sdk/*` reference pages need persona chips, or are generated reference pages exempt? All 39 authored pages are tagged and chip-synced (now lint-enforced via `npm run check-persona-chips`); the 33 generated pages are currently exempt by convention. + +Blocks: the literal reading of CON-1518's "tag every page"; the only remaining implementation wrinkle. Detail: CON-1518 (2026-06-29 comment). + +--- + +*Non-blocking follow-ups already ticketed elsewhere: `vastai verify-status` / queue-position-by-GPU-tier (product/CLI feature), CLI error-string deep-linking (docs anchors are ready).* diff --git a/review-questions.mdx b/review-questions.mdx new file mode 100644 index 00000000..5f4e064c --- /dev/null +++ b/review-questions.mdx @@ -0,0 +1,83 @@ +--- +title: "The 8 Blocking Inputs" +sidebarTitle: "Review Questions" +description: "The eight reviewer decisions and confirmations that finish the Host Docs epic. Hidden review page — not part of the docs nav; removed before merge." +"canonical": "/review-questions" +--- + + +Temporary review page (mirror of `REVIEW-QUESTIONS.md` on the PR). **Select any question text and click "💬 Comment on selection" to answer it** — your answer lands in the same feedback export as your page comments. You can also comment on the file in the PR diff, or reply on the linked Jira ticket. + + +The writing for the Host Docs epic (CON-1187) is done; each item below is a **decision, review, or confirmation** — nothing here is writing work. The first two unblock everything else. + +## Input 1. IA approval + +**Owner: Michele + docs owners · Round 0 · unblocks everything** + +Approve or modify the information architecture (CON-1518 decision points a–d): + +- (a) **Lifecycle sidebar** — Before You Host → Set Up → Verify & List → Operate → Reference, vs. some other top-level grouping. +- (b) **P0/P1/P2 priority order** — the ticket-volume-driven ordering of new docs. +- (c) **Supported Hardware as the #1 prevention doc** — even though it isn't the largest ticket bucket. +- (d) **Persona tags** — keep the visible chips (top-right of each page), restyle them, or drop to frontmatter-only convention. + +## Input 2. Review mechanics + +**Owner: Michele + docs owners** + +One small PR per sidebar group, or staged review of PR #185 with per-round checklists? PR #185 now contains all the work (former #153 is an ancestor of it). Either way, #152 and #153 should be closed as superseded. + +## Input 3. Pricing content review + +**Owner: Gobind / Solutions Engineering · Round 3 · CON-1256** + +Review the business/pricing positioning: [Pricing Your Listing](/host/pricing-your-listing), [Market Metrics](/host/market-metrics), and [Optimize Your Earnings](/host/optimization-guide). + +## Input 4. Machine-error platform behavior + +**Owner: Backend source owner (Hanran) · CON-1531** + +Seven confirmations that set the "how long it persists" copy on the [Machine Error Reference](/host/machine-errors): + +1. Is the 2026-06-24 error catalog complete for host-visible machine errors? +2. For each error, which field displays it to hosts (`error_msg`, `error_note`, `error_description`, `vm_error_msg`, `vm_error_level`, other)? +3. Which errors appear on the Machines page vs. only in a failed rental/instance detail? +4. For machine-deverifying errors, what clears the error — next clean heartbeat, successful self-test, admin action, time decay? +5. For VM-offer-only errors, what is the approximate clean-report/TTL before VM offers return? +6. Should logged-only rental-attempt messages be public host docs, or internal/support-only? +7. Should the console deep-link docs by raw error string, normalized category, or both? + +## Input 5. Installer Wizard screenshot + +**Owner: Product · Rounds 0/2** + +Approve the Host Installer Wizard (TUI) screenshot in [Installing Host Software](/host/installing-host-software#host-installer-wizard) — or supply a replacement asset. + +## Input 6. Supported Hardware sign-off + +**Owner: Product · Round 1** + +Confirm [Supported Hardware](/host/supported-hardware): exact GPU-family coverage, OS/cgroup guidance, and alignment of the CPU rule between docs, self-test #6, and vast-cli #413. Related product asks on the radar: payment/tax edge cases (incl. W-8 for non-US hosts) and datacenter requirements wording. + +## Input 7. Host Teams engineering answers + +**Owner: Engineering · Round 6 · CON-1581** + +Five answers that gate publishing [Host Teams](/host/host-teams): + +1. Individual→team migration: what happens to existing machines and accrued earnings? +2. Install-command `undefined` bug in team context — status? +3. What is the exact flow for granting machine-registration rights to a team API key? (Flagged inside the draft page itself.) +4. Are `billing_read`-only roles viable for host teams? +5. Who owns billing/payouts when machines move into a team? + +## Input 8. Persona scope ruling + +**Owner: Docs team · Round 5** + +Do the generated `host/cli/*` and `host/sdk/*` reference pages need persona chips, or are generated reference pages exempt? All 39 authored pages are tagged and chip-synced (lint-enforced); the 33 generated pages are currently exempt by convention. + +--- + +*Non-blocking follow-ups already ticketed elsewhere: `vastai verify-status` / queue-position-by-GPU-tier (product/CLI feature), CLI error-string deep-linking (docs anchors are ready).* From 28f9864506f005a8e76a200b41a39caed69f5c47 Mon Sep 17 00:00:00 2001 From: Hannes Zietsman Date: Fri, 10 Jul 2026 13:48:44 +0200 Subject: [PATCH 20/26] CON-1187 link the 8 blocking questions from the review overlay The overlay panel footer and the /__review__/ status page now point to /review-questions so reviewers discover the questions inside the kit, not only via the PR description or roadmap. Co-Authored-By: Claude Fable 5 --- review-server.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/review-server.mjs b/review-server.mjs index dd6a818b..6e56f674 100644 --- a/review-server.mjs +++ b/review-server.mjs @@ -224,6 +224,7 @@ code{background:#f0f0f6;padding:2px 5px;border-radius:4px}
Download Markdown Download JSON

+

The 8 blocking questions the team needs answered live at /review-questions — comment on them there like any other page.

When you're done reviewing, send back the CSV or the JSON file — either can be imported into Jira for record keeping.

← Back to the docs preview

`; @@ -663,6 +664,9 @@ const OVERLAY_JS = String.raw` '' + '
' + '

+Download CSV (Jira import) +Download Markdown +Download JSON +