{"openapi":"3.1.0","info":{"title":"Portveil Control Plane","version":"0.2.0"},"paths":{"/v1/accounts":{"post":{"summary":"Create Account","operationId":"create_account_v1_accounts_post","parameters":[{"name":"x-signup-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Signup-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}":{"delete":{"summary":"Delete Account","operationId":"delete_account_v1_accounts__account_id__delete","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Get Account","description":"Read the account's plan, device limit, and device count.\n\nReturns only non-sensitive summary fields — never token hashes or email.","operationId":"get_account_v1_accounts__account_id__get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"summary":"Update Account","description":"Set the account default bandwidth quota (Mbps).","operationId":"update_account_v1_accounts__account_id__patch","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/healthz":{"get":{"summary":"Healthz","description":"Public liveness check for external uptime monitors: 200 when the API\nand its database answer. Reveals nothing else.","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/servers":{"get":{"summary":"Servers","operationId":"servers_v1_servers_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/exits":{"get":{"summary":"Exits","description":"Public list of exit locations and the address traffic leaves from, for\nportveil.com/check and anyone deciding where to exit. Nothing account-specific.","operationId":"exits_v1_exits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/servers/{server_id}/peer-heartbeat":{"post":{"summary":"Peer Heartbeat","description":"Exit server reports which peer pubkeys have live WireGuard handshakes.\n\nAuthenticated with the per-server token (cls_...). This is what backs the\ndashboard's exit_confirmed flag: a device can claim protection, but only\nthe exit can confirm the tunnel exists.","operationId":"peer_heartbeat_v1_servers__server_id__peer_heartbeat_post","parameters":[{"name":"server_id","in":"path","required":true,"schema":{"type":"string","title":"Server Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeerHeartbeat"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/servers/{server_id}/traffic":{"post":{"summary":"Server Traffic","description":"Exits report this month's outbound bytes; the watcher alerts before the\nhost's included-traffic allowance runs out.","operationId":"server_traffic_v1_servers__server_id__traffic_post","parameters":[{"name":"server_id","in":"path","required":true,"schema":{"type":"string","title":"Server Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrafficReport"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/servers/{server_id}/quota-config":{"get":{"summary":"Server Quota Config","description":"Resolved per-peer bandwidth quotas for this exit server.\n\nAuthenticated with the per-server token (cls_...). The exit operator\nfetches this and feeds it to clearline/exits/apply-quotas.py:\n\n    curl -sS -H \"Authorization: Bearer $CLS_TOKEN\" \\\n      https://ctl/v1/servers/$SID/quota-config > quotas.json\n    sudo python3 apply-quotas.py --config quotas.json --iface wg0 --apply\n\nEach peer entry carries the tunnel IP and the resolved cap in Mbps\n(device override, else the account default; null = unlimited).","operationId":"server_quota_config_v1_servers__server_id__quota_config_get","parameters":[{"name":"server_id","in":"path","required":true,"schema":{"type":"string","title":"Server Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices":{"post":{"summary":"Register Device","operationId":"register_device_v1_accounts__account_id__devices_post","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRegister"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Devices","operationId":"list_devices_v1_accounts__account_id__devices_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices/{device_id}":{"patch":{"summary":"Update Device","operationId":"update_device_v1_accounts__account_id__devices__device_id__patch","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Device","description":"Delete a device and revoke its token immediately.","operationId":"delete_device_v1_accounts__account_id__devices__device_id__delete","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices/{device_id}/rotate-token":{"post":{"summary":"Rotate Device Token","description":"Rotate a device's bearer token (e.g. after suspected exposure).\nThe old token stops working immediately; the new one is shown once.","operationId":"rotate_device_token_v1_accounts__account_id__devices__device_id__rotate_token_post","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices/{device_id}/status":{"post":{"summary":"Post Status","operationId":"post_status_v1_devices__device_id__status_post","parameters":[{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusReport"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices/{device_id}/commands/pending":{"get":{"summary":"Pending","operationId":"pending_v1_devices__device_id__commands_pending_get","parameters":[{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices/{device_id}/commands/{command_id}/ack":{"post":{"summary":"Ack","operationId":"ack_v1_devices__device_id__commands__command_id__ack_post","parameters":[{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"command_id","in":"path","required":true,"schema":{"type":"string","title":"Command Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandAck"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices/{device_id}/commands":{"post":{"summary":"Enqueue","operationId":"enqueue_v1_accounts__account_id__devices__device_id__commands_post","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandEnqueue"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/commands/{command_id}":{"get":{"summary":"Command Status","operationId":"command_status_v1_accounts__account_id__commands__command_id__get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"command_id","in":"path","required":true,"schema":{"type":"string","title":"Command Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices/{device_id}/speed-history":{"get":{"summary":"Get Speed History","description":"Measured download/upload speed for the dashboard graph: one sample per exit\nheartbeat (about every 5 s), each averaged over the time since the one before.","operationId":"get_speed_history_v1_accounts__account_id__devices__device_id__speed_history_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"minutes","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Minutes"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices/{device_id}/rotation":{"get":{"summary":"Get Rotation","operationId":"get_rotation_v1_accounts__account_id__devices__device_id__rotation_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Put Rotation","description":"Move this device to the next location every N minutes, automatically.","operationId":"put_rotation_v1_accounts__account_id__devices__device_id__rotation_put","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotationPut"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/api-tokens":{"post":{"summary":"Create Api Token","description":"Mint a scoped agent token. Requires the account token itself — agent\ntokens can never mint new tokens. Plaintext is shown once.","operationId":"create_api_token_v1_accounts__account_id__api_tokens_post","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiTokenCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Api Tokens","operationId":"list_api_tokens_v1_accounts__account_id__api_tokens_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/api-tokens/{token_id}":{"delete":{"summary":"Revoke Api Token","description":"Revoke an agent token immediately. The token dies at once, even mid-use.","operationId":"revoke_api_token_v1_accounts__account_id__api_tokens__token_id__delete","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"token_id","in":"path","required":true,"schema":{"type":"string","title":"Token Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/audit-log":{"get":{"summary":"Get Audit Log","description":"Who did what, when: commands issued, tokens minted/revoked. Newest first.","operationId":"get_audit_log_v1_accounts__account_id__audit_log_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/plugins/install":{"post":{"summary":"Install Plugin","description":"Install a plugin manifest for the account. Account token only —\nagent tokens get 403. The manifest is validated (kind, semver, name,\nconfig_schema must be a valid JSON Schema, defaults must validate).","operationId":"install_plugin_v1_accounts__account_id__plugins_install_post","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PluginInstall"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/plugins":{"get":{"summary":"List Plugins","operationId":"list_plugins_v1_accounts__account_id__plugins_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/plugins/{plugin_id}":{"delete":{"summary":"Delete Plugin","description":"Delete a plugin and all its device assignments. Account token only.","operationId":"delete_plugin_v1_accounts__account_id__plugins__plugin_id__delete","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"plugin_id","in":"path","required":true,"schema":{"type":"string","title":"Plugin Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/accounts/{account_id}/devices/{device_id}/plugins":{"put":{"summary":"Set Device Plugins","description":"Replace a device's plugin assignments. control scope or higher — a\nread-scoped agent token gets 403. Each entry's config is validated\nagainst the plugin's config_schema.","operationId":"set_device_plugins_v1_accounts__account_id__devices__device_id__plugins_put","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePluginsPut"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Get Device Plugins","description":"A device's plugin assignments with merged effective configs.","operationId":"get_device_plugins_v1_accounts__account_id__devices__device_id__plugins_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AccountCreate":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"AccountCreate"},"AccountUpdate":{"properties":{"quota_mbps":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quota Mbps"}},"type":"object","title":"AccountUpdate"},"ApiTokenCreate":{"properties":{"name":{"type":"string","title":"Name"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"device_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device Id"}},"type":"object","required":["name","scopes"],"title":"ApiTokenCreate"},"CommandAck":{"properties":{"ok":{"type":"boolean","title":"Ok"},"detail":{"type":"string","title":"Detail","default":""}},"type":"object","required":["ok"],"title":"CommandAck"},"CommandEnqueue":{"properties":{"type":{"type":"string","title":"Type"},"server_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Server Id"}},"type":"object","required":["type"],"title":"CommandEnqueue"},"DevicePluginEntry":{"properties":{"plugin_id":{"type":"string","title":"Plugin Id"},"enabled":{"type":"boolean","title":"Enabled","default":true},"config":{"additionalProperties":true,"type":"object","title":"Config","default":{}}},"type":"object","required":["plugin_id"],"title":"DevicePluginEntry"},"DevicePluginsPut":{"properties":{"plugins":{"items":{"$ref":"#/components/schemas/DevicePluginEntry"},"type":"array","title":"Plugins"}},"type":"object","required":["plugins"],"title":"DevicePluginsPut"},"DeviceRegister":{"properties":{"name":{"type":"string","title":"Name"},"platform":{"type":"string","title":"Platform"},"peer_pubkey":{"type":"string","title":"Peer Pubkey"},"allow_remote":{"type":"boolean","title":"Allow Remote","default":true},"ttl_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ttl Minutes"}},"type":"object","required":["name","platform","peer_pubkey"],"title":"DeviceRegister"},"DeviceUpdate":{"properties":{"allow_remote":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Remote"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"quota_mbps":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quota Mbps"}},"type":"object","title":"DeviceUpdate"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"PeerHeartbeat":{"properties":{"peers":{"items":{"$ref":"#/components/schemas/PeerSightingIn"},"type":"array","title":"Peers"}},"type":"object","required":["peers"],"title":"PeerHeartbeat"},"PeerSightingIn":{"properties":{"peer_pubkey":{"type":"string","title":"Peer Pubkey"},"handshake_age_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Handshake Age S"},"rx_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rx Bytes"},"tx_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tx Bytes"}},"type":"object","required":["peer_pubkey"],"title":"PeerSightingIn"},"PluginInstall":{"properties":{"manifest":{"additionalProperties":true,"type":"object","title":"Manifest"}},"type":"object","required":["manifest"],"title":"PluginInstall"},"RotationPut":{"properties":{"every_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Every Minutes"},"servers":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Servers"}},"type":"object","title":"RotationPut"},"StatusReport":{"properties":{"server_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Server Id"},"connected":{"type":"boolean","title":"Connected","default":false},"handshake_age_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Handshake Age S"},"rx_bytes":{"type":"integer","title":"Rx Bytes","default":0},"tx_bytes":{"type":"integer","title":"Tx Bytes","default":0},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"}},"type":"object","title":"StatusReport"},"TrafficReport":{"properties":{"month":{"type":"string","title":"Month"},"tx_bytes":{"type":"integer","title":"Tx Bytes"}},"type":"object","required":["month","tx_bytes"],"title":"TrafficReport"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}