diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6a0981402..98cce4b6e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -6,6 +6,9 @@ + + + 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED); + return; + } if (requestCode != NOTIFICATION_PERMISSION_REQUEST) return; if (notificationPermissionRequestedByWeb) { notificationPermissionRequestedByWeb = false; diff --git a/android/app/src/test/java/com/github/aida0710/sshc/CameraRequestTest.java b/android/app/src/test/java/com/github/aida0710/sshc/CameraRequestTest.java new file mode 100644 index 000000000..10247a1a0 --- /dev/null +++ b/android/app/src/test/java/com/github/aida0710/sshc/CameraRequestTest.java @@ -0,0 +1,26 @@ +package com.github.aida0710.sshc; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class CameraRequestTest { + @Test + public void aRequestForTheCameraAloneIsForVideoOnly() { + assertTrue(CameraRequest.asksOnlyForVideo(new String[]{"android.webkit.resource.VIDEO_CAPTURE"})); + } + + @Test + public void aRequestThatAlsoAsksForTheMicrophoneIsNotForVideoOnly() { + assertFalse(CameraRequest.asksOnlyForVideo(new String[]{ + "android.webkit.resource.VIDEO_CAPTURE", "android.webkit.resource.AUDIO_CAPTURE"})); + } + + @Test + public void aRequestForAnythingElseIsNotForVideoOnly() { + assertFalse(CameraRequest.asksOnlyForVideo(new String[]{"android.webkit.resource.AUDIO_CAPTURE"})); + assertFalse(CameraRequest.asksOnlyForVideo(new String[]{})); + assertFalse(CameraRequest.asksOnlyForVideo(null)); + } +} diff --git a/api/openapi.yaml b/api/openapi.yaml index 47cb646e2..ccbb466ff 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -2443,6 +2443,39 @@ paths: "409": { $ref: "#/components/responses/Problem" } "500": { $ref: "#/components/responses/Problem" } "502": { $ref: "#/components/responses/Problem" } + /api/v1/sync/setup/export: + post: + operationId: exportSyncSetup + responses: + "200": + description: The configured target and its credentials sealed with the shared key + content: + application/json: + schema: { $ref: "#/components/schemas/SyncSetupExport" } + "401": { $ref: "#/components/responses/Problem" } + "403": { $ref: "#/components/responses/Problem" } + "409": { $ref: "#/components/responses/Problem" } + "500": { $ref: "#/components/responses/Problem" } + /api/v1/sync/setup/import: + post: + operationId: importSyncSetup + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/SyncSetupImportRequest" } + responses: + "200": + description: Exported settings opened with the shared key, verified against the target, and saved atomically + content: + application/json: + schema: { $ref: "#/components/schemas/SyncSetupResponse" } + "400": { $ref: "#/components/responses/Problem" } + "401": { $ref: "#/components/responses/Problem" } + "403": { $ref: "#/components/responses/Problem" } + "409": { $ref: "#/components/responses/Problem" } + "500": { $ref: "#/components/responses/Problem" } + "502": { $ref: "#/components/responses/Problem" } /api/v1/sync/exclusions: get: operationId: getSyncExclusions @@ -6274,6 +6307,33 @@ components: status: { $ref: "#/components/schemas/SyncStatus" } # Returned once only when the server generated the key. generatedKey: { type: string, minLength: 1 } + SyncSetupExport: + type: object + additionalProperties: false + required: [document, keyGenerated] + properties: + # 同期先と認証情報を同期鍵で封じた文書。同期鍵が無いと開けないので、ファイルにも + # QRコードにもしてよい。 + document: { type: string, minLength: 1, maxLength: 16384 } + # 同期鍵がengineの生成した形か。falseなら、文書を手に入れた人が鍵を手元で + # 試せることを画面が注意する。 + keyGenerated: { type: boolean } + # documentのQRコードのマス目。上の行から、各行は "1"(黒)と "0"(白)の並びで、 + # 周りの余白は含まない。QRコードに入り切らない長さのときは無い。 + qrRows: + type: array + minItems: 21 + maxItems: 177 + items: { type: string, minLength: 21, maxLength: 177, pattern: "^[01]+$" } + SyncSetupImportRequest: + type: object + additionalProperties: false + required: [document, key, direction] + properties: + document: { type: string, minLength: 1, maxLength: 16384 } + # 文書を開く鍵であり、そのままこのマシンの同期鍵になる。 + key: { type: string, minLength: 1, maxLength: 1024 } + direction: { $ref: "#/components/schemas/SyncDirection" } AutoSync: type: object additionalProperties: false diff --git a/docs/design.md b/docs/design.md index 3b5b27d21..f978a962c 100644 --- a/docs/design.md +++ b/docs/design.md @@ -190,6 +190,7 @@ - push ごとに `snapshots/YYYY-MM-DD-HHMMSS---.tar.gz.enc` へ日付付き候補を保存します。同じ秒の複数送信やプロセス再起動でも衝突しないよう、送信元、暗号文のdigest、プロセス内sequenceを含めます。候補を先に書き、その後ライブオブジェクトへ条件付きで書き込むため、ライブオブジェクトが部分更新されることはありません。ライブの条件付き書き込みが競合で拒否された場合は、その試行が作った候補だけを削除します。通信断などでライブ書き込みの成否を確定できない場合は証拠を失わないため候補を残し、次の周期のHEAD確認で再送信を止めます。成功したpushの履歴数にアプリケーション側の上限はなく、古いスナップショットには後から更新・削除した鍵も残ります。必要なら`snapshots/`にバケットのlifecycle ruleを設定してください。 - S3 の認証情報はマスターパスワードで暗号化し、`~/.ssh/sshc/sync-settings` に保存します。同期は `~/.ssh` 配下の通常ファイルを相対パスのまま再帰的に収集するため、ルート直下に手動配置した秘密鍵も対象です。一方、バケットへのアクセス情報、マシン固有の状態、journal・backup・history・trash、一時ファイルは明示的に除外し、symlink・socket・FIFO・device は追跡も転送もしません。`TestASnapshotTravelsBetweenTwoMachines` がルート鍵を含む往復を、`TestASnapshotCarriesTheVaultAndNotTheKeyToItsOwnBucket` が除外を検査します。 - スナップショットはマスターパスワードではなく同期専用鍵で暗号化します。既定では 120 ビット、Crockford base32 の 24 文字を 4 文字ごとに区切った 29 文字(`AB12-CD34-EF56-GH78-JK90-MN12`。区切りも鍵の一部)を生成し、利用者が指定することもできます。鍵は `sync-settings` に保存し、平文では作成時の応答に一度だけ含めます。マシンごとに異なるマスターパスワードを使用できます。過去の暗号化方式を変換する専用操作は提供しません。同期は空の保存先または現行schemaだけの保存先から始め、force pushは現行schemaの別世代を、利用者が確認したETagに対して条件付きで置き換える場合だけに使います。 +- 同期の設定は、設定済みのマシンから別のマシンへ渡せます(`POST /api/v1/sync/setup/export`、`POST /api/v1/sync/setup/import`)。書き出しは、同期先(endpoint・bucket・path・region)とS3の認証情報を、Vaultにある同期専用鍵でスナップショットと同じenvelopeに封じ、`{"sshcSyncSettings":1,"sealed":""}`の文書にします。同期専用鍵は文書に入れず、書き出すときに入力も求めません。engineは文書と一緒にQRコードのマス目(`rsc.io/qr`、誤り訂正L)を返し、QRコードに入り切らない長さならマス目を返しません。取り込みは、入力された鍵で文書を開き(鍵導出のコストはスナップショットと同じ`OpenRemote`の上限)、中身を手入力の設定と同じ`ValidateTarget`に通し、同期先が既存であることを確かめてから`CompleteSetup`で設定と鍵をまとめて保存します。同期先が空なら`sync_setup_target_empty`で断り、新しい同期先として始めません。送受信の向きはマシンごとの値なので文書に入れず、取り込む画面で選びます。QRコードの読み取りはWebの画面がカメラの映像を`jsqr`で読み、Android版はMainActivityがengineのoriginからの映像だけの要求に限ってカメラを許可します(`CameraRequest`)。 - 空の保存先へ設定を保存するときは、鍵を確かめてから、ファイルを含まないスナップショットをライブオブジェクトとして`If-None-Match: *`で置き、そのあとで設定を保存します(`remotesync.Service.placeSetupSnapshot`)。このマシンが最初に同期する前に別のマシンが同じ保存先を設定しても、そのマシンは既存の保存先と判断して同じ鍵を求め、このスナップショットを復号して確かめます。空のままにすると、別のマシンが新しい鍵を作り、あとで同期したこのマシンは鍵が合わなくなるためです。ファイルを含めないのは、除外の設定と送る内容を利用者が確かめる前に、`~/.ssh`の中身を消せない履歴へ送らないためで、同じ理由で受信専用のマシンも置きます。日付付きの履歴は置きません。ライブの書き込みに失敗したときに、履歴だけが残って参加も新規の設定もできない保存先にしないためです。置いた世代は同期状態に記録しますが、同期した操作は持たないので、画面には「まだ同期していません」と出ます。次のpushはこの世代を親にして置き換え、最初の同期の前に鍵を置き換えるとこの世代も新しい鍵で封じ直します。設定の保存に失敗したら、置いたスナップショットを消し、同期状態を元に戻します。 - Vaultは同期元で復号し、スナップショット内の `sshc/secrets.json` として格納し、同期先でそのマシンのマスターパスワードを使って再暗号化します。Vaultのファイル自体を転送すると、同期先でも同期元のマスターパスワードが必要になるためです。アーカイブ全体は同期専用鍵で暗号化します。空のVaultは同期せず、2 台目の最初の pull で不要な競合が生じないようにします。マニフェスト v6 は内容から検証できるrevision ID、親revision ID、commit message に加え、認証済みの ancestor chain を持ち、日付付き暗号化スナップショットをGit風の履歴として辿れます。読み取りは v6 だけを受け付け、ほかの schema は `snapshot_schema_unsupported` で断ります。v0.24.0 より前の sshc が書いた v5 が最新のまま残っている保存先は、送信できるマシンから強制 push で上書きします。 - 同期状態(`sshc/sync-state.json`)には、前回同期したVault文書とスニペットの文書(起動スニペットのシークレットの入力値を含む)のdigestを、Vaultの鍵で鍵付きにした値(`hmac-sha256:`)だけを残します。マスターパスワードの変更、復旧、resetで鍵が変わるときは、このマシンのVaultやスニペットが前回の同期から変わっていなければ、同じトランザクションでその値を新しい鍵の値へ書き換えます。書き換えないと、ほかのマシンだけがVaultを変えた場合も衝突になるためです。前のバージョンが書いた素のdigestは読み替えません。 diff --git a/go.mod b/go.mod index 45ace7bc0..e2c218e58 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( golang.org/x/term v0.45.0 golang.org/x/text v0.41.0 gopkg.in/yaml.v3 v3.0.1 + rsc.io/qr v0.2.0 ) require ( diff --git a/go.sum b/go.sum index 732cc4488..f51ece4be 100644 --- a/go.sum +++ b/go.sum @@ -247,3 +247,5 @@ gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= +rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= diff --git a/internal/acceptance/android_shell_test.go b/internal/acceptance/android_shell_test.go index af276d3d0..4bed5cf4b 100644 --- a/internal/acceptance/android_shell_test.go +++ b/internal/acceptance/android_shell_test.go @@ -245,7 +245,7 @@ func TestTheAndroidShellRoutesModernBackNavigationThroughTheWebView(t *testing.T } // WebViewの既定実装ではfile inputをキャンセルするため、Android shellが -// 選択・保存・renderer異常・通知permissionを明示的に仲介する形を固定する。 +// 選択・保存・renderer異常・通知permission・カメラの要求を明示的に仲介する形を固定する。 func TestTheAndroidShellBridgesDeviceSpecificWebFeatures(t *testing.T) { t.Parallel() manifest := readRepoFile(t, "android", "app", "src", "main", "AndroidManifest.xml") @@ -269,7 +269,11 @@ func TestTheAndroidShellBridgesDeviceSpecificWebFeatures(t *testing.T) { "POST_NOTIFICATIONS", "requestPermissions(", "Intent.ACTION_VIEW", - "engineOrigin.getPort() == target.getPort()", + "engineOrigin.getPort() == url.getPort()", + "if (isEngineOrigin(target)) return false;", + "onPermissionRequest(PermissionRequest request)", + "!isEngineOrigin(request.getOrigin()) || !CameraRequest.asksOnlyForVideo(request.getResources())", + "Manifest.permission.CAMERA", "setSystemBarsAppearance(", "saveWithAndroid(", "notifyAndroidTransfer(", diff --git a/internal/api/models.gen.go b/internal/api/models.gen.go index ef0e31a07..5c27b3afe 100644 --- a/internal/api/models.gen.go +++ b/internal/api/models.gen.go @@ -2034,6 +2034,20 @@ type SyncSetupCheckResponse struct { State SyncSetupTargetState `json:"state"` } +// SyncSetupExport defines model for SyncSetupExport. +type SyncSetupExport struct { + Document string `json:"document"` + KeyGenerated bool `json:"keyGenerated"` + QrRows *[]string `json:"qrRows,omitempty"` +} + +// SyncSetupImportRequest defines model for SyncSetupImportRequest. +type SyncSetupImportRequest struct { + Direction SyncDirection `json:"direction"` + Document string `json:"document"` + Key string `json:"key"` +} + // SyncSetupRequest defines model for SyncSetupRequest. type SyncSetupRequest struct { AccessKeyId *string `json:"accessKeyId,omitempty"` diff --git a/internal/httpserver/sync.go b/internal/httpserver/sync.go index c9c247e13..2640473d8 100644 --- a/internal/httpserver/sync.go +++ b/internal/httpserver/sync.go @@ -46,6 +46,8 @@ func registerSyncRoutes(engine *echo.Echo, handlers SyncHandlers) { engine.GET("/api/v1/sync", handlers.Status) engine.POST("/api/v1/sync/setup/check", handlers.CheckSetup) engine.PUT("/api/v1/sync/setup", handlers.CompleteSetup) + engine.POST("/api/v1/sync/setup/export", handlers.ExportSetup) + engine.POST("/api/v1/sync/setup/import", handlers.ImportSetup) engine.GET("/api/v1/sync/exclusions", handlers.Exclusions) engine.PUT("/api/v1/sync/exclusions", handlers.SaveExclusions) engine.PUT("/api/v1/sync/key", handlers.SetKey) diff --git a/internal/httpserver/sync_setup.go b/internal/httpserver/sync_setup.go index 0aef25ec4..7f4c33e56 100644 --- a/internal/httpserver/sync_setup.go +++ b/internal/httpserver/sync_setup.go @@ -171,25 +171,42 @@ func (h SyncHandlers) CompleteSetup(c *echo.Context) error { expected.ETag = *request.ExpectedETag } client := h.objectStoreClient(config, credentials) - err = h.Service.CompleteSetup(c.Request().Context(), config, credentials, client, expected, syncKey, func() error { + err = h.Service.CompleteSetup(c.Request().Context(), config, credentials, client, expected, syncKey, + h.persistSyncSetup(config, credentials, syncKey)) + if err != nil { + return setupFailureProblem(c, err) + } + response := h.savedSetupResponse() + if generated { + response.GeneratedKey = &syncKey + } + return c.JSON(http.StatusOK, response) +} + +// persistSyncSetup は、確かめ終えた設定を Vault に保存する関数を返す。config.Direction は +// 決め終えた向きである。 +func (h SyncHandlers) persistSyncSetup(config remotesync.Config, credentials remotesync.Credentials, syncKey string) func() error { + return func() error { return h.Vault.SetSyncSettings(secret.SyncSettings{ Endpoint: config.Endpoint, Bucket: config.Bucket, Path: config.Path, Region: config.Region, AccessKeyID: credentials.AccessKeyID, SecretAccessKey: credentials.SecretAccessKey, - Direction: string(direction), Key: syncKey, + Direction: string(config.Direction), Key: syncKey, }) - }) - if err != nil { - if vaultUnavailable(err) { - return problem(c, http.StatusConflict, "vault_locked") - } - return syncProblem(c, err) } +} + +func setupFailureProblem(c *echo.Context, err error) error { + if vaultUnavailable(err) { + return problem(c, http.StatusConflict, "vault_locked") + } + return syncProblem(c, err) +} + +// savedSetupResponse は、設定を保存し終えたあとの状態を返す。同期先が変わったので、 +// 自動同期が覚えているリモートの様子は捨てる。 +func (h SyncHandlers) savedSetupResponse() api.SyncSetupResponse { if h.Auto != nil { h.Auto.ResetRemoteCache() } - response := api.SyncSetupResponse{Status: h.statusResponse()} - if generated { - response.GeneratedKey = &syncKey - } - return c.JSON(http.StatusOK, response) + return api.SyncSetupResponse{Status: h.statusResponse()} } diff --git a/internal/httpserver/sync_setup_export.go b/internal/httpserver/sync_setup_export.go new file mode 100644 index 000000000..3c4e8941b --- /dev/null +++ b/internal/httpserver/sync_setup_export.go @@ -0,0 +1,89 @@ +package httpserver + +import ( + "net/http" + "strings" + + "github.com/labstack/echo/v5" + + "sshc/internal/api" + "sshc/internal/remotesync" +) + +// 同期の設定の書き出しと取り込み。設定済みのマシンが同期先と認証情報を同期鍵で封じた +// 文書を渡し、別のマシンは同期鍵を入力してそれを開き、同じ同期先に参加する。 + +// ExportSetup は、このマシンの同期先と認証情報を、Vault にある同期鍵で封じて返す。 +// +// 認証情報がengineの外へ出るのは、この封じた形だけである。同期鍵を知らないと開けない。 +func (h SyncHandlers) ExportSetup(c *echo.Context) error { + if h.Vault == nil { + return problem(c, http.StatusConflict, "vault_locked") + } + settings, err := h.Vault.SyncSettings() + if err != nil { + if vaultUnavailable(err) { + return problem(c, http.StatusConflict, "vault_locked") + } + return unexpectedProblem(c, "vault_unreadable", err) + } + if settings.Endpoint == "" || settings.Bucket == "" || settings.AccessKeyID == "" { + return syncProblem(c, remotesync.ErrNotConfigured) + } + if settings.Key == "" { + return problem(c, http.StatusConflict, "sync_key_missing") + } + document, err := remotesync.SealSettingsExport(remotesync.SettingsExport{ + Endpoint: settings.Endpoint, Bucket: settings.Bucket, Path: settings.Path, Region: settings.Region, + Credentials: remotesync.Credentials{ + AccessKeyID: settings.AccessKeyID, SecretAccessKey: settings.SecretAccessKey, + }, + }, settings.Key) + if err != nil { + return syncProblem(c, err) + } + response := api.SyncSetupExport{Document: document, KeyGenerated: remotesync.KeyLooksGenerated(settings.Key)} + if rows, fits := remotesync.SettingsExportQRCode(document); fits { + response.QrRows = &rows + } + return c.JSON(http.StatusOK, response) +} + +// ImportSetup は、書き出した文書を入力された鍵で開き、その同期先を確かめてから、 +// 設定と鍵をまとめて保存する。文書を開いた鍵が、そのままスナップショットを確かめる鍵になる。 +func (h SyncHandlers) ImportSetup(c *echo.Context) error { + var request api.SyncSetupImportRequest + if err := decodeJSON(c, &request); err != nil { + return problem(c, http.StatusBadRequest, "invalid_request") + } + direction, ok := remotesync.ParseDirection(string(request.Direction)) + if !ok { + return problem(c, http.StatusBadRequest, "unknown_sync_direction") + } + syncKey := strings.TrimSpace(request.Key) + if syncKey == "" || len(syncKey) > remotesync.MaxKeyLength { + return problem(c, http.StatusBadRequest, "invalid_request") + } + // 保存できないと分かっているなら、鍵導出と同期先への問い合わせを始めない。 + if h.Vault == nil || !h.Vault.Unlocked() { + return problem(c, http.StatusConflict, "vault_locked") + } + exported, err := remotesync.OpenSettingsExport(request.Document, syncKey) + if err != nil { + return syncProblem(c, err) + } + // 文書は別のマシンから届いた入力である。手で打った設定と同じ検証に通す。 + config, credentials, err := syncSetupInput(api.SyncSetupCheckRequest{ + Endpoint: exported.Endpoint, Bucket: exported.Bucket, Path: &exported.Path, Region: &exported.Region, + }, exported.Credentials) + if err != nil { + return syncProblem(c, remotesync.ErrSettingsExportRejected) + } + config.Direction = direction + client := h.objectStoreClient(config, credentials) + if err := h.Service.JoinSetup(c.Request().Context(), config, credentials, client, syncKey, + h.persistSyncSetup(config, credentials, syncKey)); err != nil { + return setupFailureProblem(c, err) + } + return c.JSON(http.StatusOK, h.savedSetupResponse()) +} diff --git a/internal/httpserver/sync_setup_export_test.go b/internal/httpserver/sync_setup_export_test.go new file mode 100644 index 000000000..a0be08154 --- /dev/null +++ b/internal/httpserver/sync_setup_export_test.go @@ -0,0 +1,171 @@ +package httpserver + +import ( + "encoding/json" + "net/http" + "strings" + "testing" + + "github.com/labstack/echo/v5" + + "sshc/internal/api" +) + +const ( + syncSetupExportPath = "/api/v1/sync/setup/export" + syncSetupImportPath = "/api/v1/sync/setup/import" +) + +// setUpFirstMachine は、空の bucket に engine を設定し、生成された同期鍵を返す。 +func setUpFirstMachine(t *testing.T, engine *echo.Echo) string { + t.Helper() + generate := checkSyncSetup(t, engine) + generate["key"] = "" + recorder := sendSync(t, engine, http.MethodPut, syncSetupPath, syncSetupBody(t, generate)) + if recorder.Code != http.StatusOK { + t.Fatalf("first machine setup = %d: %s", recorder.Code, recorder.Body.String()) + } + var created api.SyncSetupResponse + if err := json.Unmarshal(recorder.Body.Bytes(), &created); err != nil { + t.Fatal(err) + } + if created.GeneratedKey == nil { + t.Fatal("first machine setup did not return the generated key") + } + return *created.GeneratedKey +} + +func exportSyncSetup(t *testing.T, engine *echo.Echo) api.SyncSetupExport { + t.Helper() + recorder := sendSync(t, engine, http.MethodPost, syncSetupExportPath, "") + if recorder.Code != http.StatusOK { + t.Fatalf("export = %d: %s", recorder.Code, recorder.Body.String()) + } + var exported api.SyncSetupExport + if err := json.Unmarshal(recorder.Body.Bytes(), &exported); err != nil { + t.Fatal(err) + } + return exported +} + +func importSyncSetupBody(t *testing.T, document, key string) string { + t.Helper() + body, err := json.Marshal(api.SyncSetupImportRequest{Document: document, Key: key, Direction: api.SyncDirectionPull}) + if err != nil { + t.Fatal(err) + } + return string(body) +} + +func syncStatusOf(t *testing.T, engine *echo.Echo) api.SyncStatus { + t.Helper() + recorder := sendSync(t, engine, http.MethodGet, "/api/v1/sync", "") + if recorder.Code != http.StatusOK { + t.Fatalf("status = %d: %s", recorder.Code, recorder.Body.String()) + } + var status api.SyncStatus + if err := json.Unmarshal(recorder.Body.Bytes(), &status); err != nil { + t.Fatal(err) + } + return status +} + +func TestASecondMachineJoinsWithTheExportedSettingsAndTheSyncKey(t *testing.T) { + bucket := &measuredSyncBucket{} + first, _ := syncEngineOn(t, bucket) + second, _ := syncEngineOn(t, bucket) + syncKey := setUpFirstMachine(t, first) + + exported := exportSyncSetup(t, first) + if !exported.KeyGenerated { + t.Error("the generated key is reported as chosen by the user") + } + if exported.QrRows == nil { + t.Error("ordinary settings did not come with a QR code") + } + for _, plain := range []string{"s3.example.invalid", "AKID", syncKey} { + if strings.Contains(exported.Document, plain) { + t.Fatalf("the exported document shows %q in the clear", plain) + } + } + + // 鍵の前後の空白は、手で設定するときと同じく無視する。 + recorder := sendSync(t, second, http.MethodPost, syncSetupImportPath, importSyncSetupBody(t, exported.Document, " "+syncKey+"\n")) + if recorder.Code != http.StatusOK { + t.Fatalf("import = %d: %s", recorder.Code, recorder.Body.String()) + } + var joined api.SyncSetupResponse + if err := json.Unmarshal(recorder.Body.Bytes(), &joined); err != nil { + t.Fatal(err) + } + if joined.GeneratedKey != nil { + t.Error("the second machine generated a key of its own") + } + status := joined.Status + if !status.Configured || !status.KeyConfigured || status.Endpoint != "https://s3.example.invalid" || + status.Bucket != "sshc" || status.Direction != api.SyncDirectionPull { + t.Fatalf("status after import = %+v", status) + } + if status.AccessKeySuffix == nil || *status.AccessKeySuffix != "AKID" { + t.Fatalf("access key suffix after import = %v, want AKID", status.AccessKeySuffix) + } + + // 取り込んだ鍵がこのマシンの同期鍵になっているので、そのまま受信の内容を確かめられる。 + if recorder := sendSync(t, second, http.MethodPost, "/api/v1/sync/pull", `{"apply":false}`); recorder.Code != http.StatusOK { + t.Fatalf("pull preview after import = %d: %s", recorder.Code, recorder.Body.String()) + } +} + +func TestImportSavesNothingWhenItIsRefused(t *testing.T) { + bucket := &measuredSyncBucket{} + first, _ := syncEngineOn(t, bucket) + syncKey := setUpFirstMachine(t, first) + document := exportSyncSetup(t, first).Document + + refusals := map[string]struct { + bucket *measuredSyncBucket + document string + key string + status int + code string + }{ + "another sync key": {bucket, document, "ZZ12-CD34-EF56-GH78-JK90-MN12", http.StatusForbidden, "wrong_passphrase"}, + "not an exported document": {bucket, `{"endpoint":"https://s3.example.invalid"}`, syncKey, http.StatusBadRequest, "sync_settings_export_rejected"}, + "the target has been wiped": {&measuredSyncBucket{}, document, syncKey, http.StatusConflict, "sync_setup_target_empty"}, + } + for name, refusal := range refusals { + t.Run(name, func(t *testing.T) { + second, _ := syncEngineOn(t, refusal.bucket) + recorder := sendSync(t, second, http.MethodPost, syncSetupImportPath, importSyncSetupBody(t, refusal.document, refusal.key)) + if recorder.Code != refusal.status || !strings.Contains(recorder.Body.String(), refusal.code) { + t.Fatalf("import = %d %s, want %d %s", recorder.Code, recorder.Body.String(), refusal.status, refusal.code) + } + if status := syncStatusOf(t, second); status.Configured { + t.Fatalf("a refused import left the machine configured: %+v", status) + } + if objects := len(refusal.bucket.objects); refusal.bucket != bucket && objects != 0 { + t.Fatalf("a refused import wrote %d objects to the empty target", objects) + } + }) + } +} + +func TestExportIsRefusedBeforeSyncIsSetUp(t *testing.T) { + engine, _ := syncEngine(t) + recorder := sendSync(t, engine, http.MethodPost, syncSetupExportPath, "") + if recorder.Code != http.StatusConflict || !strings.Contains(recorder.Body.String(), "sync_not_configured") { + t.Fatalf("export = %d %s, want 409 sync_not_configured", recorder.Code, recorder.Body.String()) + } +} + +func TestExportWarnsAboutAKeyTheUserChose(t *testing.T) { + engine, _ := syncEngine(t) + chosen := checkSyncSetup(t, engine) + chosen["key"] = "a key the user chose" + if recorder := sendSync(t, engine, http.MethodPut, syncSetupPath, syncSetupBody(t, chosen)); recorder.Code != http.StatusOK { + t.Fatalf("setup = %d: %s", recorder.Code, recorder.Body.String()) + } + if exportSyncSetup(t, engine).KeyGenerated { + t.Fatal("a key the user chose is reported as generated") + } +} diff --git a/internal/remotesync/binding.go b/internal/remotesync/binding.go index e32a1a245..1c21a53ea 100644 --- a/internal/remotesync/binding.go +++ b/internal/remotesync/binding.go @@ -126,7 +126,7 @@ func (s *Service) Direction() Direction { } // Target は、この実行が指しているエンドポイントとバケットを、表示のために返す。 -// アクセスキーと秘密が何かによって返されることは決してない。 +// アクセスキーと秘密は返さない。 func (s *Service) Target() (endpoint, bucket, path, region string) { s.mutex.Lock() defer s.mutex.Unlock() @@ -135,7 +135,8 @@ func (s *Service) Target() (endpoint, bucket, path, region string) { // AccessKeySuffix returns only the final five characters needed to identify // the configured account in setup and status output. The complete identifier -// and secret access key never leave the engine. +// and secret access key leave the engine only sealed with the sync key, in +// exported settings (SealSettingsExport). func (s *Service) AccessKeySuffix() string { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/internal/remotesync/failure.go b/internal/remotesync/failure.go index 731bdc51a..ec941ac3a 100644 --- a/internal/remotesync/failure.go +++ b/internal/remotesync/failure.go @@ -72,6 +72,7 @@ var failureRules = []failureRule{ {is(ErrRecoveryTargetChange), Failure{"sync_key_recovery_target_change", FailureConflict}}, {is(ErrSetupTargetChanged), Failure{"sync_setup_target_changed", FailureConflict}}, {is(ErrSetupTargetIncomplete), Failure{"sync_setup_target_incomplete", FailureConflict}}, + {is(ErrSetupTargetEmpty), Failure{"sync_setup_target_empty", FailureConflict}}, {is(ErrHistoryKeyLossConfirmation), Failure{"sync_history_key_loss_confirmation_required", FailureConflict}}, {is(ErrNothingToPush), Failure{"sync_nothing_to_push", FailureConflict}}, {is(ErrNoSnapshot), Failure{"sync_no_snapshot", FailureNotFound}}, @@ -82,6 +83,7 @@ var failureRules = []failureRule{ {is(ErrHistoryTarget), Failure{"sync_history_target_invalid", FailureInvalid}}, {is(ErrCommitMessage), Failure{"sync_commit_message_invalid", FailureInvalid}}, {is(ErrInvalidIgnoreRules), Failure{"sync_ignore_invalid", FailureInvalid}}, + {is(ErrSettingsExportRejected), Failure{"sync_settings_export_rejected", FailureInvalid}}, {is(ErrWrongPassphrase), Failure{"wrong_passphrase", FailureForbidden}}, {is(ErrWeakPassphrase), Failure{"passphrase_too_short", FailureInvalid}}, {is(ErrCostRefused), Failure{"snapshot_cost_refused", FailureConflict}}, diff --git a/internal/remotesync/key.go b/internal/remotesync/key.go index a5a82496f..763d3b610 100644 --- a/internal/remotesync/key.go +++ b/internal/remotesync/key.go @@ -54,3 +54,21 @@ func NewKey() (string, error) { } return out.String(), nil } + +// KeyLooksGenerated は、syncKey が NewKey の作る形(120 ビット)かを報告する。 +// +// 同期の設定を書き出すと、その文書を手に入れた人は同期鍵を手元で好きなだけ試せる。 +// 生成した鍵なら総当たりは現実的でないが、利用者が決めた鍵はそうとは限らないので、 +// 書き出しの画面はこの答えが false のときに注意を出す。 +func KeyLooksGenerated(syncKey string) bool { + groups := strings.Split(syncKey, "-") + if len(groups) != KeyBytes*8/5/keyGroup { + return false + } + for _, group := range groups { + if len(group) != keyGroup || strings.Trim(group, keyAlphabet) != "" { + return false + } + } + return true +} diff --git a/internal/remotesync/settings_export.go b/internal/remotesync/settings_export.go new file mode 100644 index 000000000..b54a60d9d --- /dev/null +++ b/internal/remotesync/settings_export.go @@ -0,0 +1,162 @@ +package remotesync + +import ( + "encoding/base64" + "encoding/json" + "errors" + "strings" + + "rsc.io/qr" + + "sshc/internal/envelope" + "sshc/internal/strictjson" +) + +// 同期の設定の書き出し。設定済みのマシンが同期先とその認証情報を同期鍵で封じた文書を作り、 +// 別のマシンは同期鍵を入力してそれを開く。文書はファイルにも QR コードにもなる。 +// +// 封じるのは同期鍵である。取り込むマシンはどのみち同期鍵を入力するので、文書を開くために +// 別の秘密を覚える必要が無い。書き出すマシンは同期鍵を Vault に持っているので、何も +// 入力せずに書き出せる。 +// +// {"sshcSyncSettings":1,"sealed":""} +// +// 外側は、開く前に「これは同期の設定である」と見分けるためだけにあり、認証されない。 +// 信じてよいのは、封を開いて得た中身だけである。 + +const ( + // settingsExportFormat は、外側の文書の形の版。 + settingsExportFormat = 1 + // settingsExportKind は、封の中身が同期の設定であることを示す印。同じ同期鍵は + // スナップショットも封じるので、スナップショットの封を設定として読まないために置く。 + settingsExportKind = "sshc-sync-settings" + // MaxSettingsExportLength は、取り込む文書として受け付ける長さの上限。入力の上限 + // いっぱいの設定(endpoint 2048、bucket と path 255、認証情報 512 ずつ)を封じて + // base64 にしても 6 KiB に届かないので、その倍より少し上に置く。 + MaxSettingsExportLength = 16 << 10 +) + +// ErrSettingsExportRejected は、取り込む文書が同期の設定の書き出しとして読めないことを +// 報告する。同期鍵が違うときは ErrWrongPassphrase を返し、これとは分ける。 +var ErrSettingsExportRejected = errors.New("this is not an exported sshc synchronization settings document") + +// SettingsExport は、別のマシンが同じ同期先に参加するために要るもの。 +// +// 送受信の向き(Direction)は含めない。マシンごとに決める値で、受信専用のマシンを +// 足すために主のマシンの設定を写す、という使い方があるからである。 +type SettingsExport struct { + Endpoint string + Bucket string + Path string + Region string + Credentials Credentials +} + +type settingsExportDocument struct { + Format int `json:"sshcSyncSettings"` + Sealed string `json:"sealed"` +} + +type settingsExportContent struct { + Kind string `json:"kind"` + Endpoint string `json:"endpoint"` + Bucket string `json:"bucket"` + Path string `json:"path"` + Region string `json:"region"` + AccessKeyID string `json:"accessKeyId"` + SecretAccessKey string `json:"secretAccessKey"` +} + +// SealSettingsExport は、settings を syncKey で封じた文書を返す。 +func SealSettingsExport(settings SettingsExport, syncKey string) (string, error) { + plaintext, err := json.Marshal(settingsExportContent{ + Kind: settingsExportKind, + Endpoint: settings.Endpoint, Bucket: settings.Bucket, Path: settings.Path, Region: settings.Region, + AccessKeyID: settings.Credentials.AccessKeyID, SecretAccessKey: settings.Credentials.SecretAccessKey, + }) + if err != nil { + return "", err + } + defer clear(plaintext) + derivedKey, err := envelope.Derive(syncKey) + if err != nil { + return "", err + } + defer derivedKey.Destroy() + sealed, err := derivedKey.Seal(plaintext) + if err != nil { + return "", err + } + document, err := json.Marshal(settingsExportDocument{ + Format: settingsExportFormat, Sealed: base64.StdEncoding.EncodeToString(sealed), + }) + if err != nil { + return "", err + } + return string(document), nil +} + +// OpenSettingsExport は、SealSettingsExport が書いた文書を syncKey で開く。 +// +// 文書は別のマシンから届く。鍵導出のコストは文書が決めるので、スナップショットと同じ +// OpenRemote の上限で開く。返す設定は検証していない。呼び出し側が ValidateTarget に通す。 +func OpenSettingsExport(document, syncKey string) (SettingsExport, error) { + if len(document) > MaxSettingsExportLength { + return SettingsExport{}, ErrSettingsExportRejected + } + var outer settingsExportDocument + if err := strictjson.Decode([]byte(strings.TrimSpace(document)), &outer); err != nil { + return SettingsExport{}, ErrSettingsExportRejected + } + if outer.Format != settingsExportFormat { + return SettingsExport{}, ErrSettingsExportRejected + } + sealed, err := base64.StdEncoding.DecodeString(outer.Sealed) + if err != nil { + return SettingsExport{}, ErrSettingsExportRejected + } + plaintext, derivedKey, err := envelope.OpenRemote(sealed, syncKey) + if errors.Is(err, envelope.ErrWrongPassphrase) { + return SettingsExport{}, ErrWrongPassphrase + } + if err != nil { + return SettingsExport{}, ErrSettingsExportRejected + } + derivedKey.Destroy() + defer clear(plaintext) + var content settingsExportContent + if err := strictjson.Decode(plaintext, &content); err != nil || content.Kind != settingsExportKind { + return SettingsExport{}, ErrSettingsExportRejected + } + return SettingsExport{ + Endpoint: content.Endpoint, Bucket: content.Bucket, Path: content.Path, Region: content.Region, + Credentials: Credentials{AccessKeyID: content.AccessKeyID, SecretAccessKey: content.SecretAccessKey}, + }, nil +} + +// SettingsExportQRCode は、document を QR コードにしたときのマス目を、上の行から返す。 +// 各行は '1'(黒)と '0'(白)の並びで、周りの余白は含まない。QR コードに入り切らない +// 長さなら ok は false で、そのときはファイルで渡す。 +// +// 誤り訂正は最も低い L にする。画面に出したものをその場で読むので汚れや欠けが無く、 +// マス目が粗いほうがカメラで読みやすい。 +func SettingsExportQRCode(document string) (rows []string, ok bool) { + code, err := qr.Encode(document, qr.L) + if err != nil { + return nil, false + } + rows = make([]string, code.Size) + var row strings.Builder + for y := range code.Size { + row.Reset() + for x := range code.Size { + if code.Black(x, y) { + row.WriteByte('1') + } else { + row.WriteByte('0') + } + } + rows[y] = row.String() + } + return rows, true +} diff --git a/internal/remotesync/settings_export_test.go b/internal/remotesync/settings_export_test.go new file mode 100644 index 000000000..85b1faf88 --- /dev/null +++ b/internal/remotesync/settings_export_test.go @@ -0,0 +1,141 @@ +package remotesync_test + +import ( + "encoding/base64" + "encoding/json" + "errors" + "strings" + "testing" + + "sshc/internal/envelope" + "sshc/internal/remotesync" +) + +const settingsExportTestKey = "AB12-CD34-EF56-GH78-JK90-MN12" + +func exportedSettingsForTest() remotesync.SettingsExport { + return remotesync.SettingsExport{ + Endpoint: "https://s3.example.com", Bucket: "team-bucket", Path: "team/laptop", Region: "auto", + Credentials: remotesync.Credentials{AccessKeyID: "AKIAEXAMPLE", SecretAccessKey: "secret-access-key"}, + } +} + +func TestExportedSettingsOpenWithTheSyncKeyThatSealedThem(t *testing.T) { + want := exportedSettingsForTest() + document, err := remotesync.SealSettingsExport(want, settingsExportTestKey) + if err != nil { + t.Fatalf("SealSettingsExport: %v", err) + } + for _, plain := range []string{want.Endpoint, want.Bucket, want.Credentials.AccessKeyID, want.Credentials.SecretAccessKey} { + if strings.Contains(document, plain) { + t.Fatalf("the exported document shows %q in the clear", plain) + } + } + + got, err := remotesync.OpenSettingsExport(document+"\n", settingsExportTestKey) + if err != nil { + t.Fatalf("OpenSettingsExport: %v", err) + } + if got != want { + t.Fatalf("opened settings = %+v, want %+v", got, want) + } +} + +func TestExportedSettingsRefuseAnotherSyncKey(t *testing.T) { + document, err := remotesync.SealSettingsExport(exportedSettingsForTest(), settingsExportTestKey) + if err != nil { + t.Fatalf("SealSettingsExport: %v", err) + } + _, err = remotesync.OpenSettingsExport(document, "ZZ12-CD34-EF56-GH78-JK90-MN12") + if !errors.Is(err, remotesync.ErrWrongPassphrase) { + t.Fatalf("error = %v, want ErrWrongPassphrase", err) + } +} + +func TestDocumentsThatAreNotExportedSettingsAreRejected(t *testing.T) { + derivedKey, err := envelope.Derive(settingsExportTestKey) + if err != nil { + t.Fatalf("Derive: %v", err) + } + sealedDocument := func(plaintext string) string { + sealed, err := derivedKey.Seal([]byte(plaintext)) + if err != nil { + t.Fatalf("Seal: %v", err) + } + document, err := json.Marshal(map[string]any{ + "sshcSyncSettings": 1, "sealed": base64.StdEncoding.EncodeToString(sealed), + }) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + return string(document) + } + documents := map[string]string{ + "empty": "", + "not JSON": "sshc", + "another format version": `{"sshcSyncSettings":2,"sealed":""}`, + "an unknown outer field": `{"sshcSyncSettings":1,"sealed":"","extra":true}`, + "sealed is not base64": `{"sshcSyncSettings":1,"sealed":"***"}`, + "sealed is not an envelope": `{"sshcSyncSettings":1,"sealed":"c3NoYw=="}`, + "sealed content of other kind": sealedDocument(`{"kind":"snapshot","endpoint":"https://s3.example.com","bucket":"sshc","path":"","region":"auto","accessKeyId":"a","secretAccessKey":"b"}`), + "sealed content is not JSON": sealedDocument("archive bytes"), + "longer than the limit": `{"sshcSyncSettings":1,"sealed":"` + strings.Repeat("A", remotesync.MaxSettingsExportLength) + `"}`, + } + for name, document := range documents { + t.Run(name, func(t *testing.T) { + _, err := remotesync.OpenSettingsExport(document, settingsExportTestKey) + if !errors.Is(err, remotesync.ErrSettingsExportRejected) { + t.Fatalf("error = %v, want ErrSettingsExportRejected", err) + } + }) + } +} + +func TestExportedSettingsFitInAQRCodeUnlessTheyAreTooLong(t *testing.T) { + document, err := remotesync.SealSettingsExport(exportedSettingsForTest(), settingsExportTestKey) + if err != nil { + t.Fatalf("SealSettingsExport: %v", err) + } + rows, ok := remotesync.SettingsExportQRCode(document) + if !ok { + t.Fatalf("a %d-character document did not fit in a QR code", len(document)) + } + for _, row := range rows { + if len(row) != len(rows) || strings.Trim(row, "01") != "" { + t.Fatalf("row %q is not a square row of modules", row) + } + } + // 左上の位置合わせの印は、黒い 7 マスの辺で始まる。 + if !strings.HasPrefix(rows[0], "1111111") { + t.Fatalf("first row %q does not start with a finder pattern", rows[0]) + } + + longest := exportedSettingsForTest() + longest.Endpoint = "https://" + strings.Repeat("a", remotesync.MaxEndpointLength-len("https://")) + longest.Credentials.SecretAccessKey = strings.Repeat("s", remotesync.MaxSecretAccessKeyLength) + document, err = remotesync.SealSettingsExport(longest, settingsExportTestKey) + if err != nil { + t.Fatalf("SealSettingsExport: %v", err) + } + if _, ok := remotesync.SettingsExportQRCode(document); ok { + t.Fatalf("a %d-character document was reported to fit in a QR code", len(document)) + } + if len(document) > remotesync.MaxSettingsExportLength { + t.Fatalf("the longest exported document is %d characters, over the import limit", len(document)) + } +} + +func TestOnlyKeysInTheGeneratedShapeLookGenerated(t *testing.T) { + generated, err := remotesync.NewKey() + if err != nil { + t.Fatalf("NewKey: %v", err) + } + if !remotesync.KeyLooksGenerated(generated) { + t.Fatalf("generated key %q does not look generated", generated) + } + for _, chosen := range []string{"correct horse battery", "AB12-CD34-EF56-GH78-JK90", "AB12-CD34-EF56-GH78-JK90-MNIL", "ab12-cd34-ef56-gh78-jk90-mn12"} { + if remotesync.KeyLooksGenerated(chosen) { + t.Fatalf("chosen key %q looks generated", chosen) + } + } +} diff --git a/internal/remotesync/setup.go b/internal/remotesync/setup.go index 36f6432ba..06a2b0f27 100644 --- a/internal/remotesync/setup.go +++ b/internal/remotesync/setup.go @@ -30,6 +30,8 @@ const ( var ( ErrSetupTargetChanged = errors.New("the synchronization target changed after connection check") ErrSetupTargetIncomplete = errors.New("the synchronization target has history but no live snapshot") + // ErrSetupTargetEmpty は、書き出した設定の指す同期先にスナップショットが無いことを報告する。 + ErrSetupTargetEmpty = errors.New("the synchronization target named by the exported settings holds no snapshot") ) type SetupInspection struct { @@ -129,6 +131,26 @@ func (s *Service) CompleteSetup(ctx context.Context, config Config, credentials return nil } +// JoinSetup は、書き出した設定(SettingsExport)の同期先に、それを開いた syncKey で参加する。 +// +// 書き出したマシンは設定のときにスナップショットを置いているので、同期先は既存のはずである。 +// 空なら、書き出したあとで同期先が消されたか別の場所を指している。ここで新しい同期先として +// 始めると、書き出したマシンと別々の同期先になるので断る。 +func (s *Service) JoinSetup(ctx context.Context, config Config, credentials objectstore.Credentials, + client *objectstore.Client, syncKey string, persist func() error) error { + inspection, err := InspectSetupTarget(ctx, client, normalizeConfig(config)) + if err != nil { + return err + } + switch inspection.State { + case SetupTargetEmpty: + return ErrSetupTargetEmpty + case SetupTargetIncomplete: + return ErrSetupTargetIncomplete + } + return s.CompleteSetup(ctx, config, credentials, client, inspection, syncKey, persist) +} + // placeSetupSnapshot は、空の同期先へファイルを含まないスナップショットを置き、その世代を // このマシンの同期の記録にする。返す関数は、置いたスナップショットを消し、記録を元に戻す。 // diff --git a/pages/en/features/sync.md b/pages/en/features/sync.md index 682eda84b..369d80d19 100644 --- a/pages/en/features/sync.md +++ b/pages/en/features/sync.md @@ -35,6 +35,17 @@ Saving places one snapshot without files in the target. Devices added later are Enter the same bucket path and encryption key. When a remote snapshot already exists, sshc saves the configuration only after the key can decrypt it. Use another path if you intend to create a separate dataset. +A device that is already set up can also hand its settings over, so the endpoint, bucket, and access keys are not typed again. + +1. On the device that is set up, open Manage sync settings and choose Export under Export settings. Nothing is typed +2. Scan the QR code with the new device, or choose Save to file and move the file to the new device +3. On the new device, open Sync and choose Scan QR code or Choose file +4. Enter the encryption key and choose Import + +The exported file and QR code hold the bucket connection details and the S3 credentials, encrypted with the encryption key. The encryption key itself is not in them. The new device decrypts the settings with the key you enter, and saves them only after the same key also decrypts the snapshot at the target. + +Scanning a QR code uses the camera. The Android app asks for the camera permission the first time you scan. Where no camera is available, hand the settings over as a file. + Choose bidirectional, send-only, or receive-only sync. - Bidirectional sync receives remote updates and sends local changes. diff --git a/pages/en/reference/security.md b/pages/en/reference/security.md index 7fe25fa72..f0636d1bc 100644 --- a/pages/en/reference/security.md +++ b/pages/en/reference/security.md @@ -91,6 +91,14 @@ Sync is end-to-end encrypted (E2EE): only devices configured with the same encry Anyone with the ciphertext can try guessing the encryption key offline. Use the random key sshc generates rather than a short typed string. +### Exported sync settings + +The file and QR code made by Export settings hold the endpoint, bucket, path in the bucket, region, and the S3 access key ID and secret access key. They are encrypted with the encryption key in the same way as a snapshot (AES-256-GCM and Argon2id). The encryption key is not in them. This encrypted form is the only way the S3 credentials leave the sshc engine. + +Anyone who obtains the file or the QR code can try guessing the encryption key offline, as with a snapshot's ciphertext, and a guessed key also reveals the S3 credentials. When you chose the encryption key yourself, the export screen shows a warning. Delete the exported file once the new device has it. + +The Android app uses the camera only while it is scanning a QR code. The video is neither stored nor sent anywhere. + ### What remains after replacing the sync key Replacing the sync key re-encrypts only the latest snapshot with the new key. The history already in the bucket stays encrypted with the previous key: sshc can no longer read it, but anyone who knows the previous key still can. Snapshots pushed after the replacement are encrypted with the new key. diff --git a/pages/en/reference/troubleshooting.md b/pages/en/reference/troubleshooting.md index 42f67fb79..eeac403e7 100644 --- a/pages/en/reference/troubleshooting.md +++ b/pages/en/reference/troubleshooting.md @@ -120,6 +120,8 @@ Sync failures show a cause, and **More details** shows a stable code. | `unsafe_bucket_name`, `unsafe_object_path` | The bucket name or the path in the bucket uses characters other than letters, digits, hyphens, periods and underscores | | `sync_push_refused`, `sync_apply_refused` | The machine's direction is Receive only or Send only. Change the direction to send or apply | | `wrong_passphrase` | The encryption key does not match the data at that target | +| `sync_settings_export_rejected` | The imported file or QR code cannot be read as sync settings exported by sshc. Export the settings again on a device that is set up | +| `sync_setup_target_empty` | The target in the exported settings has no sync data: it was emptied or changed after the export. Export the settings again on a device that is set up | | `passphrase_too_short` | A typed key is shorter than 12 characters | | `snapshot_schema_unsupported` | The remote snapshot is in a format this sshc cannot read (older than v6, or newer). If it is newer, update every device that shares the target. If it is older, overwrite it with Force Push from a device allowed to send | | `snapshot_rejected` | The snapshot decrypted but failed validation (corrupt or tampered). Restore another generation from History or push again from a sending device. It also appears when an earlier sshc release pushed, from Linux or macOS, a file whose name contains a character Windows does not allow in file names, such as `:` or `?`. Update sshc on the sending device, push, rename the file that `sync_local_path_unportable` shows, and push again | diff --git a/pages/features/sync.md b/pages/features/sync.md index d18d0fedc..307bbc3cf 100644 --- a/pages/features/sync.md +++ b/pages/features/sync.md @@ -35,6 +35,17 @@ sshcは同期用のクラウドストレージを提供せず、同期データ 追加するマシンにも、同じS3バケット、オブジェクトのパス、暗号化キーを設定します。リモートに既存のスナップショットがある場合は、正しいキーで復号できたことを確認してから保存できます。データを分ける場合は、別のバケット内パスを指定できます。 +設定済みのマシンから設定を渡すこともできます。この方法では、エンドポイント、バケット、アクセスキーを入力し直さずに済みます。 + +1. 設定済みのマシンで、[同期設定を管理]の[設定を書き出す]から[書き出す]を選ぶ。入力するものはない +2. 表示されたQRコードを追加するマシンで読み取るか、[ファイルに保存]で保存したファイルを追加するマシンへ渡す +3. 追加するマシンのSyncで、[QRコードを読み取る]か[ファイルを選択]を選ぶ +4. 暗号化キーを入力して[取り込む]を選ぶ + +書き出したファイルとQRコードには、バケットの接続情報とS3の認証情報が、暗号化キーで暗号化された状態で入ります。暗号化キーそのものは入りません。追加するマシンでは、入力した暗号化キーで設定を復号し、保存先のスナップショットも同じキーで復号できることを確認してから保存します。 + +QRコードの読み取りにはカメラを使います。Androidのアプリは、最初に読み取るときにカメラの権限を求めます。カメラを使えない場合は、ファイルで渡してください。 + 同期方向は、送受信、送信専用、受信専用から選べます。 - 送受信では、リモートの更新を受け取り、ローカルの変更も送信します diff --git a/pages/reference/security.md b/pages/reference/security.md index d922d46d3..a96d495fa 100644 --- a/pages/reference/security.md +++ b/pages/reference/security.md @@ -116,6 +116,14 @@ SSH設定に`ProxyCommand`がある場合、sshcは接続時にそのコマン 暗号文を取得した第三者は、オフラインで暗号化キーの総当たりを試せます。手入力した短い文字列ではなく、sshcが生成したランダムな暗号化キーを使用してください。 +### 書き出した同期設定 + +[設定を書き出す]で作るファイルとQRコードには、エンドポイント、バケット、バケット内のパス、リージョン、S3のアクセスキーIDとシークレットアクセスキーが入ります。これらはスナップショットと同じ方式(AES-256-GCMとArgon2id)で、暗号化キーを使って暗号化します。暗号化キーは入りません。S3の認証情報がsshcエンジンの外へ出るのは、この暗号化された形だけです。 + +ファイルやQRコードを取得した第三者は、スナップショットの暗号文と同じように、オフラインで暗号化キーの総当たりを試せます。暗号化キーを知られると、S3の認証情報も読み取られます。暗号化キーを自分で決めている場合は、書き出しの画面に注意を表示します。書き出したファイルは、追加するマシンへ渡したあとに削除してください。 + +Androidのアプリがカメラを使うのは、QRコードを読み取っている間だけです。映像は保存も送信もしません。 + ### 同期キーを置き換えたあとに残るもの 同期キーを置き換えると、sshcは最新のスナップショットだけを新しいキーで暗号化し直します。バケットに残っている過去の履歴は、以前のキーで暗号化されたままです。sshcからは読み取れなくなりますが、以前のキーを知っている人は引き続き復号できます。置き換えたあとに送信するスナップショットは、新しいキーで暗号化されます。 diff --git a/pages/reference/troubleshooting.md b/pages/reference/troubleshooting.md index 7eaa74852..dc9bd4d2d 100644 --- a/pages/reference/troubleshooting.md +++ b/pages/reference/troubleshooting.md @@ -134,6 +134,8 @@ Vaultのロックが解除されているか、同期方向が合っているか | `unsafe_bucket_name` / `unsafe_object_path` | バケット名、またはバケット内のパスに使用できない文字がある | 英数字、ハイフン、ピリオド、アンダースコアだけで入力する | | `sync_push_refused` / `sync_apply_refused` | 同期の方向が「受信のみ」のマシンで送信した、または「送信のみ」のマシンで適用した | [同期の方向]を変更する | | `wrong_passphrase` | 同じ保存先を使うマシンの暗号化キーが一致していない | 最初のマシンで保管した暗号化キーを設定し直す | +| `sync_settings_export_rejected` | 取り込んだファイルやQRコードが、sshcで書き出した同期設定として読み取れない | 設定済みのマシンで、同期設定をもう一度書き出す | +| `sync_setup_target_empty` | 書き出した同期設定の保存先に同期データがない。書き出したあとで保存先を空にしたか、別の保存先へ変更した | 設定済みのマシンで、同期設定をもう一度書き出す | | `passphrase_too_short` | 入力した暗号化キーが12文字未満 | sshcが生成した暗号化キーを使う | | `snapshot_schema_unsupported` | リモートのスナップショットが、このバージョンのsshcでは読み込めない形式(v6より古い、または新しい) | 新しい形式なら、同じ保存先を使うすべてのマシンを同じバージョンへ更新する。古い形式なら、送信できるマシンからForce Pushで上書きする | | `snapshot_rejected` | スナップショットを復号できたが、内容の検証に失敗した(破損、または改ざん)。前のバージョンのsshcがLinuxやmacOSから送ったスナップショットに、`:`や`?`などWindowsのファイル名に使えない文字を含む名前のファイルがある場合も、この表示になる | 履歴から別のスナップショットを復元するか、送信できるマシンからpushし直す。名前が原因なら、送ったマシンのsshcを更新してpushし、`sync_local_path_unportable`に表示されたファイルの名前を変更してから、もう一度pushする | diff --git a/web/package-lock.json b/web/package-lock.json index 29042a5fe..c6bec5284 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -15,6 +15,7 @@ "@xterm/addon-serialize": "0.15.0-beta.330", "@xterm/addon-webgl": "0.20.0-beta.329", "@xterm/xterm": "6.1.0-beta.336", + "jsqr": "1.4.0", "lucide-react": "1.48.0", "monaco-editor": "0.56.0", "react": "19.2.8", @@ -2306,6 +2307,12 @@ "dev": true, "license": "MIT" }, + "node_modules/jsqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz", + "integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==", + "license": "Apache-2.0" + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", diff --git a/web/package.json b/web/package.json index 67792157f..fdf941fe6 100644 --- a/web/package.json +++ b/web/package.json @@ -22,6 +22,7 @@ "@xterm/addon-serialize": "0.15.0-beta.330", "@xterm/addon-webgl": "0.20.0-beta.329", "@xterm/xterm": "6.1.0-beta.336", + "jsqr": "1.4.0", "lucide-react": "1.48.0", "monaco-editor": "0.56.0", "react": "19.2.8", diff --git a/web/src/api/schema.d.ts b/web/src/api/schema.d.ts index e90fa2f6f..2fe54ef16 100644 --- a/web/src/api/schema.d.ts +++ b/web/src/api/schema.d.ts @@ -1328,6 +1328,38 @@ export interface paths { patch?: never; trace?: never; }; + "/api/v1/sync/setup/export": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["exportSyncSetup"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v1/sync/setup/import": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["importSyncSetup"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/v1/sync/exclusions": { parameters: { query?: never; @@ -4109,6 +4141,16 @@ export interface components { status: components["schemas"]["SyncStatus"]; generatedKey?: string; }; + SyncSetupExport: { + document: string; + keyGenerated: boolean; + qrRows?: string[]; + }; + SyncSetupImportRequest: { + document: string; + key: string; + direction: components["schemas"]["SyncDirection"]; + }; AutoSync: { enabled: boolean; /** @enum {string} */ @@ -8319,6 +8361,60 @@ export interface operations { 502: components["responses"]["Problem"]; }; }; + exportSyncSetup: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The configured target and its credentials sealed with the shared key */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SyncSetupExport"]; + }; + }; + 401: components["responses"]["Problem"]; + 403: components["responses"]["Problem"]; + 409: components["responses"]["Problem"]; + 500: components["responses"]["Problem"]; + }; + }; + importSyncSetup: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SyncSetupImportRequest"]; + }; + }; + responses: { + /** @description Exported settings opened with the shared key, verified against the target, and saved atomically */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SyncSetupResponse"]; + }; + }; + 400: components["responses"]["Problem"]; + 401: components["responses"]["Problem"]; + 403: components["responses"]["Problem"]; + 409: components["responses"]["Problem"]; + 500: components["responses"]["Problem"]; + 502: components["responses"]["Problem"]; + }; + }; getSyncExclusions: { parameters: { query?: never; diff --git a/web/src/api/sync.ts b/web/src/api/sync.ts index 77c663fa6..0210fb908 100644 --- a/web/src/api/sync.ts +++ b/web/src/api/sync.ts @@ -9,6 +9,8 @@ export type SyncSetupCheckRequest = components["schemas"]["SyncSetupCheckRequest export type SyncSetupCheckResponse = components["schemas"]["SyncSetupCheckResponse"]; export type SyncSetupRequest = components["schemas"]["SyncSetupRequest"]; export type SyncSetupResponse = components["schemas"]["SyncSetupResponse"]; +export type SyncSetupExport = components["schemas"]["SyncSetupExport"]; +export type SyncSetupImportRequest = components["schemas"]["SyncSetupImportRequest"]; export type SyncDirection = components["schemas"]["SyncDirection"]; export type SnapshotSummary = components["schemas"]["SnapshotSummary"]; export type SyncOperation = components["schemas"]["SyncOperation"]; @@ -50,6 +52,8 @@ export type SyncApi = { settings: SyncSetupCheckRequest, ): Promise; completeSyncSetup(settings: SyncSetupRequest): Promise; + exportSyncSetup(): Promise; + importSyncSetup(request: SyncSetupImportRequest): Promise; syncExclusions(): Promise; saveSyncExclusions(document: string): Promise; syncPushDraft(): Promise; @@ -98,6 +102,10 @@ function validateSyncPushDraft(value: unknown): SyncPushDraft { return validateOpenAPISchema("SyncPushDraft", value); } +function validateSyncSetupExport(value: unknown): SyncSetupExport { + return validateOpenAPISchema("SyncSetupExport", value); +} + function validateSyncExclusions(value: unknown): SyncExclusions { return validateOpenAPISchema("SyncExclusions", value); } @@ -134,6 +142,16 @@ export const syncApi: SyncApi = { await putJSON("/api/v1/sync/setup", settings, locallyExplainedSyncFailures), ); }, + async exportSyncSetup() { + return validateSyncSetupExport( + await postJSON("/api/v1/sync/setup/export", undefined, undefined, locallyExplainedSyncFailures), + ); + }, + async importSyncSetup(request) { + return validateSyncSetup( + await postJSON("/api/v1/sync/setup/import", request, undefined, locallyExplainedSyncFailures), + ); + }, async syncExclusions() { return validateSyncExclusions( await apiClient.read("/api/v1/sync/exclusions"), diff --git a/web/src/api/validators.generated.ts b/web/src/api/validators.generated.ts index c2375a402..173781914 100644 --- a/web/src/api/validators.generated.ts +++ b/web/src/api/validators.generated.ts @@ -11,8 +11,8 @@ type Endpoint = Readonly<{ noContent?: readonly string[]; }>; -const componentSchemas = {"About":{"additionalProperties":false,"properties":{"arch":{"type":"string"},"commitTime":{"description":"VCS commit time recorded by Go; not the compilation time.","format":"date-time","type":"string"},"executable":{"type":"string"},"goVersion":{"type":"string"},"manager":{"enum":["homebrew","install.sh","install.ps1","unknown"],"type":"string"},"modified":{"type":"boolean"},"os":{"type":"string"},"revision":{"type":"string"},"version":{"type":"string"}},"required":["version","revision","goVersion","os","arch","manager","executable"],"type":"object"},"AddRemoteEngineRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"}},"required":["alias"],"type":"object"},"AgentIdentitiesResponse":{"additionalProperties":false,"properties":{"agentAvailable":{"type":"boolean"},"id":{"type":"string"},"identities":{"items":{"$ref":"#/components/schemas/AgentIdentity"},"type":"array"}},"required":["id","agentAvailable","identities"],"type":"object"},"AgentIdentity":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"bits":{"type":"integer"},"comment":{"type":"string"},"fingerprint":{"type":"string"}},"required":["bits","fingerprint","comment","algorithm"],"type":"object"},"AliasRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"}},"required":["alias"],"type":"object"},"AppearanceSettings":{"additionalProperties":false,"properties":{"accent":{"pattern":"^(lavender|blue|sky|pink|mono|#[0-9a-f]{6})$","type":"string"},"emptyTerminalArt":{"enum":["none","network","contours","globe","aquarium"],"type":"string"},"reduceMotion":{"type":"boolean"},"theme":{"pattern":"^[a-z0-9-]{1,32}$","type":"string"}},"type":"object"},"AssignCredentialRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":128,"minLength":1,"type":"string"},"subject":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["subject","name"],"type":"object"},"AuthenticationRequest":{"additionalProperties":false,"properties":{"acknowledgeExecutable":{"type":"boolean"},"alias":{"maxLength":64,"minLength":1,"type":"string"}},"required":["alias","acknowledgeExecutable"],"type":"object"},"AuthenticationResponse":{"additionalProperties":false,"properties":{"authenticated":{"type":"boolean"},"detail":{"type":"string"},"method":{"type":"string"},"outcome":{"type":"string"},"truncated":{"type":"boolean"}},"required":["outcome","authenticated","method","detail","truncated"],"type":"object"},"AutoSync":{"additionalProperties":false,"properties":{"at":{"type":"string"},"detail":{"type":"string"},"enabled":{"type":"boolean"},"phase":{"enum":["idle","running","blocked","failed"],"type":"string"}},"required":["enabled","phase"],"type":"object"},"AutoSyncRequest":{"additionalProperties":false,"properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"type":"object"},"BackgroundSettings":{"additionalProperties":false,"properties":{"capacityMiB":{"maximum":1024,"minimum":1,"type":"integer"}},"type":"object"},"BootstrapResponse":{"additionalProperties":false,"properties":{"browserToken":{"maxLength":43,"minLength":43,"pattern":"^[A-Za-z0-9_-]{43}$","type":"string"},"csrfToken":{"maxLength":43,"minLength":43,"pattern":"^[A-Za-z0-9_-]{43}$","type":"string"}},"required":["csrfToken"],"type":"object"},"ChangeMasterPasswordRequest":{"additionalProperties":false,"properties":{"current":{"maxLength":1024,"type":"string"},"next":{"maxLength":1024,"type":"string"}},"required":["current","next"],"type":"object"},"ChangeMasterPasswordResult":{"additionalProperties":false,"properties":{"vault":{"$ref":"#/components/schemas/PasswordVaultStatus"}},"required":["vault"],"type":"object"},"ChangePassphraseRequest":{"additionalProperties":false,"properties":{"currentPassphrase":{"maxLength":1024,"type":"string"},"newPassphrase":{"maxLength":1024,"type":"string"},"unencrypted":{"type":"boolean"}},"required":["currentPassphrase","newPassphrase","unencrypted"],"type":"object"},"ChangePassphraseResponse":{"additionalProperties":false,"properties":{"encrypted":{"type":"boolean"},"id":{"type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"relativePath":{"type":"string"},"transactionId":{"type":"string"}},"required":["id","relativePath","encrypted","notes","transactionId"],"type":"object"},"ChangedResponse":{"additionalProperties":false,"properties":{"changed":{"type":"boolean"}},"required":["changed"],"type":"object"},"ComplexityNote":{"additionalProperties":false,"properties":{"code":{"type":"string"},"condition":{"type":"string"},"detail":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"}},"required":["code","path","line","condition","detail"],"type":"object"},"ConfigCheckResponse":{"additionalProperties":false,"properties":{"diagnostics":{"items":{"$ref":"#/components/schemas/ConfigDiagnostic"},"type":"array"},"files":{"items":{"$ref":"#/components/schemas/ConfigFileSummary"},"type":"array"},"root":{"type":"string"}},"required":["root","files","diagnostics"],"type":"object"},"ConfigDiagnostic":{"additionalProperties":false,"properties":{"code":{"type":"string"},"detail":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"},"severity":{"type":"string"}},"required":["severity","code","path","line","detail"],"type":"object"},"ConfigFileSummary":{"additionalProperties":false,"properties":{"editable":{"type":"boolean"},"includes":{"type":"integer"},"loads":{"type":"integer"},"missing":{"type":"boolean"},"path":{"type":"string"}},"required":["path","editable","missing","loads","includes"],"type":"object"},"ConflictReport":{"additionalProperties":false,"properties":{"baseDigest":{"type":"string"},"diskDigest":{"type":"string"},"externalChange":{"items":{"$ref":"#/components/schemas/DiffLine"},"type":"array"},"localChange":{"items":{"$ref":"#/components/schemas/DiffLine"},"type":"array"},"path":{"type":"string"}},"required":["path","externalChange","localChange"],"type":"object"},"ConnectionHistory":{"additionalProperties":false,"properties":{"hours":{"items":{"$ref":"#/components/schemas/ConnectionHour"},"maxItems":8928,"type":"array"},"recent":{"items":{"$ref":"#/components/schemas/RecentConnection"},"maxItems":20,"type":"array"}},"required":["hours","recent"],"type":"object"},"ConnectionHour":{"additionalProperties":false,"properties":{"aliases":{"items":{"maxLength":64,"minLength":1,"type":"string"},"maxItems":32,"type":"array"},"connections":{"minimum":1,"type":"integer"},"hour":{"format":"date-time","type":"string"}},"required":["hour","connections","aliases"],"type":"object"},"ConnectionIdentityFileChange":{"discriminator":{"mapping":{"inherit":"#/components/schemas/ConnectionInherit","set":"#/components/schemas/ConnectionIdentityFileSet"},"propertyName":"action"},"oneOf":[{"$ref":"#/components/schemas/ConnectionIdentityFileSet"},{"$ref":"#/components/schemas/ConnectionInherit"}],"x-go-type":"json.RawMessage"},"ConnectionIdentityFileSet":{"additionalProperties":false,"properties":{"action":{"enum":["set"],"type":"string","x-go-type":"string"},"keyId":{"maxLength":32,"minLength":32,"type":"string"}},"required":["action","keyId"],"type":"object"},"ConnectionInherit":{"additionalProperties":false,"properties":{"action":{"enum":["inherit"],"type":"string","x-go-type":"string"}},"required":["action"],"type":"object"},"ConnectionKeyPassphraseSetDedicated":{"additionalProperties":false,"properties":{"keyId":{"maxLength":32,"minLength":32,"type":"string"},"kind":{"enum":["set_dedicated"],"type":"string","x-go-type":"string"},"passphrase":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["kind","keyId","passphrase"],"type":"object"},"ConnectionKeyPassphraseUnchanged":{"additionalProperties":false,"properties":{"kind":{"enum":["unchanged"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"ConnectionPortChange":{"discriminator":{"mapping":{"inherit":"#/components/schemas/ConnectionInherit","set":"#/components/schemas/ConnectionPortSet"},"propertyName":"action"},"oneOf":[{"$ref":"#/components/schemas/ConnectionPortSet"},{"$ref":"#/components/schemas/ConnectionInherit"}],"x-go-type":"json.RawMessage"},"ConnectionPortSet":{"additionalProperties":false,"properties":{"action":{"enum":["set"],"type":"string","x-go-type":"string"},"value":{"maximum":65535,"minimum":1,"type":"integer"}},"required":["action","value"],"type":"object"},"ConnectionStringChange":{"discriminator":{"mapping":{"inherit":"#/components/schemas/ConnectionInherit","set":"#/components/schemas/ConnectionStringSet"},"propertyName":"action"},"oneOf":[{"$ref":"#/components/schemas/ConnectionStringSet"},{"$ref":"#/components/schemas/ConnectionInherit"}],"x-go-type":"json.RawMessage"},"ConnectionStringSet":{"additionalProperties":false,"properties":{"action":{"enum":["set"],"type":"string","x-go-type":"string"},"value":{"maxLength":255,"type":"string"}},"required":["action","value"],"type":"object"},"CreateAskOnConnectAuthentication":{"additionalProperties":false,"properties":{"kind":{"enum":["ask_on_connect"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"CreateConnectionAuthentication":{"discriminator":{"mapping":{"ask_on_connect":"#/components/schemas/CreateAskOnConnectAuthentication","dedicated_password":"#/components/schemas/CreateDedicatedPasswordAuthentication","identity_file":"#/components/schemas/CreateIdentityFileAuthentication","new_shared_password":"#/components/schemas/CreateNewSharedPasswordAuthentication","saved_password":"#/components/schemas/CreateSavedPasswordAuthentication"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/CreateDedicatedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateSavedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateNewSharedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateIdentityFileAuthentication"},{"$ref":"#/components/schemas/CreateAskOnConnectAuthentication"}],"x-go-type":"json.RawMessage"},"CreateConnectionRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"authentication":{"$ref":"#/components/schemas/CreateConnectionAuthentication"},"group":{"maxLength":400,"type":"string"},"hostName":{"maxLength":255,"minLength":1,"type":"string"},"port":{"maximum":65535,"minimum":1,"type":"integer"},"proxyJump":{"maxLength":64,"minLength":1,"type":"string"},"user":{"maxLength":255,"type":"string"}},"required":["alias","hostName","authentication"],"type":"object"},"CreateConnectionResponse":{"additionalProperties":false,"properties":{"identity":{"$ref":"#/components/schemas/HostIdentity"},"preview":{"$ref":"#/components/schemas/SavePreview"},"transactionId":{"type":"string"}},"required":["transactionId","identity","preview"],"type":"object"},"CreateDedicatedPasswordAuthentication":{"additionalProperties":false,"properties":{"kind":{"enum":["dedicated_password"],"type":"string","x-go-type":"string"},"password":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["kind","password"],"type":"object"},"CreateIdentityFileAuthentication":{"additionalProperties":false,"properties":{"keyId":{"maxLength":32,"minLength":32,"type":"string"},"kind":{"enum":["identity_file"],"type":"string","x-go-type":"string"}},"required":["kind","keyId"],"type":"object"},"CreateNewSharedPasswordAuthentication":{"additionalProperties":false,"properties":{"credential":{"maxLength":128,"minLength":1,"type":"string"},"kind":{"enum":["new_shared_password"],"type":"string","x-go-type":"string"},"password":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["kind","credential","password"],"type":"object"},"CreateSavedPasswordAuthentication":{"additionalProperties":false,"properties":{"credential":{"maxLength":128,"minLength":1,"type":"string"},"kind":{"enum":["saved_password"],"type":"string","x-go-type":"string"}},"required":["kind","credential"],"type":"object"},"CreateTerminalWorkspaceRequest":{"additionalProperties":false,"properties":{"hidden":{"type":"boolean"},"name":{"maxLength":64,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"Credential":{"additionalProperties":false,"properties":{"hosts":{"items":{"type":"string"},"type":"array"},"kind":{"enum":["password","key_passphrase","totp"],"type":"string"},"name":{"type":"string"},"uses":{"items":{"type":"string"},"type":"array"}},"required":["kind","name","uses","hosts"],"type":"object"},"CredentialList":{"additionalProperties":false,"properties":{"credentials":{"items":{"$ref":"#/components/schemas/Credential"},"type":"array"},"dedicatedKeyPassphrases":{"items":{"$ref":"#/components/schemas/DedicatedKeyPassphraseUsage"},"type":"array"},"keyHostUsageComplete":{"type":"boolean"}},"required":["credentials","dedicatedKeyPassphrases","keyHostUsageComplete"],"type":"object"},"DedicatedKeyPassphraseUsage":{"additionalProperties":false,"properties":{"hosts":{"items":{"type":"string"},"type":"array"},"key":{"type":"string"}},"required":["key","hosts"],"type":"object"},"Diagnostic":{"additionalProperties":false,"properties":{"absolute":{"type":"string"},"code":{"type":"string"},"detail":{"type":"string"},"external":{"type":"boolean"},"line":{"type":"integer"},"path":{"type":"string"},"severity":{"type":"string"}},"required":["severity","code"],"type":"object"},"DiffLine":{"additionalProperties":false,"properties":{"newLine":{"type":"integer"},"oldLine":{"type":"integer"},"op":{"type":"string"},"text":{"type":"string"}},"required":["op","text"],"type":"object"},"EditRequest":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"base":{"type":"string"},"bases":{"description":"The file each move_hosts or delete_hosts target lives in, with the contents the client loaded, once per file.","items":{"$ref":"#/components/schemas/FileBase"},"maxItems":256,"minItems":1,"type":"array"},"comment":{"type":"string"},"destinationBase":{"type":"string"},"destinationGroup":{"maxLength":400,"type":"string"},"destinationPath":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/FieldEdit"},"type":"array"},"groups":{"items":{"$ref":"#/components/schemas/GroupMetadata"},"maxItems":256,"type":"array"},"groupsBase":{"items":{"$ref":"#/components/schemas/GroupMetadata"},"maxItems":256,"type":"array"},"hostMetadata":{"$ref":"#/components/schemas/HostMetadata"},"hostMetadataBase":{"$ref":"#/components/schemas/HostMetadata"},"hosts":{"description":"Targets for move_hosts or delete_hosts. Each path must have exactly one entry in bases.","items":{"$ref":"#/components/schemas/HostIdentity"},"maxItems":256,"minItems":1,"type":"array"},"kind":{"type":"string"},"newAlias":{"type":"string"},"path":{"type":"string"},"raw":{"type":"string"}},"required":["kind"],"type":"object"},"Effective":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"entries":{"items":{"$ref":"#/components/schemas/EffectiveEntry"},"type":"array"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"}},"required":["alias","entries"],"type":"object"},"EffectiveChange":{"additionalProperties":false,"properties":{"after":{"items":{"type":"string"},"type":"array"},"afterSources":{"items":{"$ref":"#/components/schemas/Source"},"type":"array"},"before":{"items":{"type":"string"},"type":"array"},"beforeSources":{"items":{"$ref":"#/components/schemas/Source"},"type":"array"},"keyword":{"type":"string"}},"required":["keyword","before","after"],"type":"object"},"EffectiveDiff":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"changes":{"items":{"$ref":"#/components/schemas/EffectiveChange"},"type":"array"}},"required":["alias","changes"],"type":"object"},"EffectiveEntry":{"additionalProperties":false,"properties":{"keyword":{"type":"string"},"source":{"$ref":"#/components/schemas/Source"},"values":{"items":{"type":"string"},"type":"array"}},"required":["keyword","values","source"],"type":"object"},"EffectiveResponse":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"complexities":{"items":{"$ref":"#/components/schemas/ComplexityNote"},"type":"array"},"executableDirectives":{"items":{"$ref":"#/components/schemas/ExecutableDirective"},"type":"array"},"route":{"items":{"$ref":"#/components/schemas/JumpStage"},"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/ValueSource"},"type":"array"}},"required":["alias","executableDirectives","sources","complexities","route"],"type":"object"},"EmbeddedTerminal":{"additionalProperties":false,"properties":{"appearance":{"$ref":"#/components/schemas/TerminalAppearance"},"browserScrollbackLines":{"maximum":100000,"minimum":1000,"type":"integer"},"commandMarks":{"type":"boolean"},"commandScrollbarMarks":{"type":"boolean"},"composerEnterSends":{"type":"boolean"},"copyOnSelect":{"type":"boolean"},"fontSize":{"maximum":32,"minimum":8,"type":"integer"},"jisYenBackslash":{"type":"boolean"},"ligatures":{"type":"boolean"},"localShellProfile":{"pattern":"^[a-z0-9-]{1,64}$","type":"string"},"maxSessions":{"maximum":200,"minimum":1,"type":"integer"},"retainedTerminals":{"maximum":99,"minimum":1,"type":"integer"},"rightClickPaste":{"type":"boolean"},"runningCommandTitle":{"type":"boolean"},"scrollbackBytes":{"maximum":4194304,"minimum":16384,"type":"integer"},"startDirectory":{"maxLength":4096,"type":"string"},"statusLine":{"type":"boolean"},"stickyCommand":{"type":"boolean"},"webgl":{"type":"boolean"}},"type":"object"},"ExecutableDirective":{"additionalProperties":false,"properties":{"command":{"type":"string"},"condition":{"type":"string"},"keyword":{"type":"string"},"line":{"type":"integer"},"onConnect":{"type":"boolean"},"overridable":{"type":"boolean"},"path":{"type":"string"}},"required":["keyword","command","path","line","condition","onConnect","overridable"],"type":"object"},"FieldEdit":{"additionalProperties":false,"properties":{"action":{"type":"string"},"keyword":{"type":"string"},"line":{"type":"integer"},"values":{"items":{"type":"string"},"type":"array"}},"required":["action"],"type":"object"},"FileBase":{"additionalProperties":false,"properties":{"base":{"type":"string"},"path":{"type":"string"}},"required":["path","base"],"type":"object"},"FileContents":{"additionalProperties":false,"properties":{"contents":{"type":"string"},"digest":{"type":"string"},"editable":{"type":"boolean"},"exists":{"type":"boolean"},"file":{"$ref":"#/components/schemas/FileRef"}},"required":["file","contents","digest","editable","exists"],"type":"object"},"FileDiff":{"additionalProperties":false,"properties":{"created":{"type":"boolean"},"lines":{"items":{"$ref":"#/components/schemas/DiffLine"},"type":"array"},"newDigest":{"type":"string"},"oldDigest":{"type":"string"},"path":{"type":"string"},"removed":{"type":"boolean"},"truncated":{"type":"boolean"}},"required":["path","lines"],"type":"object"},"FileNode":{"additionalProperties":false,"properties":{"editable":{"type":"boolean"},"file":{"$ref":"#/components/schemas/FileRef"},"includes":{"items":{"$ref":"#/components/schemas/IncludeReference"},"type":"array"},"loads":{"type":"integer"},"missing":{"type":"boolean"}},"required":["file","editable","loads"],"type":"object"},"FileRef":{"additionalProperties":false,"properties":{"absolute":{"type":"string"},"external":{"type":"boolean"},"path":{"type":"string"}},"required":["absolute"],"type":"object"},"FileTransferSettings":{"additionalProperties":false,"properties":{"excludePatterns":{"description":"Folder contents to exclude. Basenames match at any depth; patterns containing / start at the selected folder. Supports * and ? only. Defaults to no exclusions.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"maxConcurrent":{"maximum":8,"minimum":1,"type":"integer"},"speedLimitBytesPerSecond":{"format":"int64","maximum":1073741824,"minimum":0,"type":"integer"}},"type":"object"},"FormField":{"additionalProperties":false,"properties":{"category":{"type":"string"},"dangerous":{"type":"boolean"},"duplicate":{"type":"boolean"},"editable":{"type":"boolean"},"keyword":{"type":"string"},"line":{"type":"integer"},"values":{"items":{"type":"string"},"type":"array"}},"required":["line","keyword","values","category","editable"],"type":"object"},"GenerateKeyRequest":{"additionalProperties":false,"properties":{"algorithm":{"maxLength":16,"minLength":1,"type":"string"},"bits":{"type":"integer"},"comment":{"maxLength":127,"type":"string"},"fileName":{"maxLength":64,"minLength":1,"type":"string"},"group":{"maxLength":400,"type":"string"},"passphrase":{"maxLength":1024,"type":"string"},"unencrypted":{"type":"boolean"}},"required":["algorithm","fileName","comment","passphrase","unencrypted"],"type":"object"},"GenerateKeyResponse":{"additionalProperties":false,"properties":{"bits":{"type":"integer"},"encrypted":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"keyType":{"type":"string"},"publicRelativePath":{"type":"string"},"relativePath":{"type":"string"},"transactionId":{"type":"string"}},"required":["id","relativePath","publicRelativePath","fingerprint","keyType","bits","encrypted","transactionId"],"type":"object"},"GlobalFileTransferSettings":{"additionalProperties":false,"properties":{"autoReconnect":{"type":"boolean"},"clearCompletedAfterSeconds":{"maximum":86400,"minimum":0,"type":"integer"},"maxReconnectAttempts":{"maximum":10,"minimum":0,"type":"integer"}},"type":"object"},"GlobalSettings":{"additionalProperties":false,"properties":{"backgrounds":{"$ref":"#/components/schemas/BackgroundSettings"},"fileTransfers":{"$ref":"#/components/schemas/GlobalFileTransferSettings"},"language":{"enum":["en","ja"],"type":"string"},"terminal":{"$ref":"#/components/schemas/GlobalTerminalSettings"},"updateNoticeFoldedRelease":{"pattern":"^[0-9A-Za-z.+-]{1,64}$","type":"string"}},"type":"object"},"GlobalTerminalSettings":{"additionalProperties":false,"properties":{"osc52":{"type":"boolean"},"reconnect":{"maximum":5,"minimum":0,"type":"integer"},"sessionLog":{"type":"boolean"},"sessionLogRules":{"maxLength":8192,"type":"string"},"verbosity":{"maximum":3,"minimum":0,"type":"integer"}},"type":"object"},"GroupDeleteRequest":{"additionalProperties":false,"properties":{"destination":{"maxLength":400,"type":"string"},"name":{"maxLength":400,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"GroupMetadata":{"additionalProperties":false,"properties":{"colour":{"type":"string"},"hidden":{"type":"boolean"},"name":{"type":"string"},"order":{"type":"integer"},"settings":{"items":{"$ref":"#/components/schemas/Setting"},"type":"array"}},"required":["name"],"type":"object"},"GroupRenameRequest":{"additionalProperties":false,"properties":{"from":{"maxLength":400,"minLength":1,"type":"string"},"to":{"maxLength":400,"minLength":1,"type":"string"}},"required":["from","to"],"type":"object"},"GroupView":{"additionalProperties":false,"properties":{"colour":{"type":"string"},"directory":{"type":"string"},"directoryPresent":{"type":"boolean"},"keyDirectory":{"type":"string"},"memberCount":{"type":"integer"},"name":{"type":"string"},"order":{"type":"integer"},"parent":{"type":"string"},"settings":{"items":{"$ref":"#/components/schemas/Setting"},"type":"array"}},"required":["name","directory","keyDirectory","memberCount","directoryPresent"],"type":"object"},"HardwareCommandRequest":{"additionalProperties":false,"properties":{"algorithm":{"maxLength":16,"minLength":1,"type":"string"},"comment":{"maxLength":127,"type":"string"},"fileName":{"maxLength":64,"minLength":1,"type":"string"},"group":{"maxLength":400,"type":"string"}},"required":["algorithm","fileName","comment"],"type":"object"},"HardwareCommandResponse":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"command":{"items":{"type":"string"},"type":"array"},"note":{"type":"string"}},"required":["algorithm","command","note"],"type":"object"},"HealthResponse":{"additionalProperties":false,"properties":{"status":{"enum":["ok"],"type":"string","x-go-type":"string"},"version":{"minLength":1,"type":"string"}},"required":["status","version"],"type":"object"},"HistoryEntry":{"additionalProperties":false,"properties":{"finishedAt":{"type":"string"},"id":{"type":"string"},"operation":{"type":"string"},"paths":{"items":{"type":"string"},"type":"array"},"restorable":{"items":{"type":"string"},"type":"array"},"startedAt":{"type":"string"},"status":{"type":"string"}},"required":["id","operation","status","startedAt","paths"],"type":"object"},"HistoryList":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/HistoryEntry"},"type":"array"}},"required":["entries"],"type":"object"},"HostDetail":{"additionalProperties":false,"properties":{"effective":{"$ref":"#/components/schemas/Effective"},"file":{"$ref":"#/components/schemas/FileContents"},"form":{"$ref":"#/components/schemas/HostForm"},"metadata":{"$ref":"#/components/schemas/HostMetadata"}},"required":["form","metadata","effective","file"],"type":"object"},"HostEntry":{"additionalProperties":false,"properties":{"duplicate":{"type":"boolean"},"editable":{"type":"boolean"},"file":{"$ref":"#/components/schemas/FileRef"},"group":{"type":"string"},"hostName":{"description":"Resolved SSH destination hostname used by this alias.","type":"string"},"identity":{"$ref":"#/components/schemas/HostIdentity"},"line":{"type":"integer"},"negated":{"type":"boolean"},"patterns":{"items":{"type":"string"},"type":"array"},"port":{"description":"Resolved SSH port used by this alias.","type":"string"},"proxyJump":{"description":"Resolved ProxyJump of this alias, as ssh would use it (comma-separated hops). Absent when the alias connects directly.","type":"string"},"user":{"description":"Resolved remote account used by this alias.","type":"string"},"wildcard":{"type":"boolean"}},"required":["identity","file","line","patterns","editable"],"type":"object"},"HostForm":{"additionalProperties":false,"properties":{"comment":{"description":"The comment lines attached above the Host line, with the '#' markers stripped. Empty when the block has none. ssh_config has no trailing comment syntax, so only whole lines are ever attached.","type":"string"},"commentLines":{"description":"How many physical lines the attached comment occupies. A client rewriting the whole file needs the count to include those lines; it cannot be derived from comment, whose markers and indentation were stripped.","type":"integer"},"entry":{"$ref":"#/components/schemas/HostEntry"},"fields":{"items":{"$ref":"#/components/schemas/FormField"},"type":"array"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"raw":{"type":"string"}},"required":["entry","fields","raw","comment","commentLines"],"type":"object"},"HostIdentity":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"path":{"maxLength":512,"type":"string"}},"required":["path","alias"],"type":"object"},"HostMetadata":{"additionalProperties":false,"properties":{"appearance":{"$ref":"#/components/schemas/TerminalAppearance"},"colour":{"type":"string"},"detectedOS":{"description":"Last operating system detected after connecting.","enum":["","server","linux","amazonlinux","ubuntu","debian","redhat","fedora","centos","rocky","almalinux","arch","alpine","opensuse","freebsd","macos","windows"],"type":"string"},"detectedOSBinding":{"maxLength":64,"type":"string"},"encoding":{"description":"Text encoding used by this SSH terminal. Omitted means UTF-8.","enum":["utf-8","shift_jis","euc-jp","iso-2022-jp"],"type":"string"},"identity":{"$ref":"#/components/schemas/HostIdentity"},"order":{"type":"integer"},"orphan":{"type":"boolean"},"os":{"description":"Home icon override. Empty or omitted enables automatic detection.","enum":["","server","linux","amazonlinux","ubuntu","debian","redhat","fedora","centos","rocky","almalinux","arch","alpine","opensuse","freebsd","macos","windows"],"type":"string"},"passwordSaveDeclinedBinding":{"maxLength":64,"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"tmux":{"type":"boolean"},"vpn":{"maxLength":48,"type":"string"}},"required":["identity"],"type":"object"},"IKEv2Profile":{"additionalProperties":false,"properties":{"authentication":{"maxLength":16,"type":"string"},"caCertificate":{"maxLength":16384,"type":"string"},"esp":{"maxLength":256,"type":"string"},"identity":{"maxLength":256,"minLength":1,"type":"string"},"ike":{"maxLength":256,"type":"string"},"server":{"maxLength":320,"minLength":1,"type":"string"},"serverIdentity":{"maxLength":256,"type":"string"}},"required":["server","authentication","identity"],"type":"object"},"IncludeReference":{"additionalProperties":false,"properties":{"condition":{"type":"string"},"line":{"type":"integer"},"matches":{"items":{"$ref":"#/components/schemas/FileRef"},"type":"array"},"pattern":{"type":"string"}},"required":["line","pattern"],"type":"object"},"InterfaceSettings":{"additionalProperties":false,"properties":{"hideSFTPThumbnails":{"type":"boolean"},"hideTouchLinkUnderlines":{"type":"boolean"},"keepScreenAwake":{"type":"boolean"},"keyBarPreset":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"notificationSound":{"$ref":"#/components/schemas/NotificationSound"},"shortcutPreset":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"touchMode":{"enum":["touch","mouse"],"type":"string"}},"type":"object"},"InterfaceState":{"additionalProperties":false,"properties":{"collapsedTerminalWorkspaces":{"maxLength":65536,"type":"string"},"runTarget":{"maxLength":65536,"type":"string"},"sftpPanes":{"maxLength":65536,"type":"string"},"sftpQueueView":{"maxLength":65536,"type":"string"},"showHiddenTerminalWorkspaces":{"maxLength":65536,"type":"string"}},"type":"object"},"IssueActionRequest":{"additionalProperties":false,"properties":{"kind":{"maxLength":64,"minLength":1,"type":"string"},"target":{"maxLength":49152,"minLength":1,"type":"string"}},"required":["kind","target"],"type":"object"},"IssueActionResponse":{"additionalProperties":false,"properties":{"expiresAt":{"minLength":1,"type":"string"},"token":{"maxLength":43,"minLength":43,"type":"string"}},"required":["token","expiresAt"],"type":"object"},"JumpStage":{"additionalProperties":false,"properties":{"complex":{"type":"boolean"},"depth":{"type":"integer"},"hop":{"type":"string"},"hostname":{"type":"string"},"order":{"type":"integer"},"parent":{"type":"string"},"port":{"type":"string"},"user":{"type":"string"}},"required":["order","depth","parent","hop","hostname","user","port","complex"],"type":"object"},"KeyAlgorithmsResponse":{"additionalProperties":false,"properties":{"source":{"type":"string"},"variants":{"items":{"$ref":"#/components/schemas/KeyVariant"},"type":"array"}},"required":["variants","source"],"type":"object"},"KeyBarKey":{"additionalProperties":false,"properties":{"enter":{"type":"boolean"},"key":{"enum":["DirectInput","Control","Alt","Escape","Tab","Shift+Tab","Enter","Backspace","Delete","Insert","ArrowLeft","ArrowUp","ArrowDown","ArrowRight","Home","End","PageUp","PageDown","Ctrl+C","Ctrl+D","Ctrl+Z","Ctrl+L","Ctrl+R","Ctrl+A","Ctrl+E","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12"],"type":"string"},"label":{"maxLength":8,"minLength":1,"type":"string"},"text":{"maxLength":1000,"minLength":1,"type":"string"}},"type":"object"},"KeyBarPreset":{"additionalProperties":false,"properties":{"id":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"keys":{"items":{"$ref":"#/components/schemas/KeyBarKey"},"maxItems":16,"type":"array"},"moreKeys":{"items":{"$ref":"#/components/schemas/KeyBarKey"},"maxItems":48,"type":"array"},"name":{"maxLength":80,"minLength":1,"type":"string"}},"required":["id","name","keys","moreKeys"],"type":"object"},"KeyCertificate":{"additionalProperties":false,"properties":{"keyId":{"type":"string"},"neverExpires":{"type":"boolean"},"principals":{"items":{"type":"string"},"type":"array"},"signedKeyFingerprint":{"type":"string"},"signedKeyType":{"type":"string"},"validBefore":{"type":"integer"}},"required":["keyId","principals","validBefore","neverExpires","signedKeyType","signedKeyFingerprint"],"type":"object"},"KeyInventoryResponse":{"additionalProperties":false,"properties":{"agentAvailable":{"type":"boolean"},"agentDelegations":{"items":{"$ref":"#/components/schemas/KeyReference"},"type":"array"},"agentIdentities":{"items":{"$ref":"#/components/schemas/AgentIdentity"},"type":"array"},"items":{"items":{"$ref":"#/components/schemas/KeyItem"},"type":"array"},"unreadable":{"items":{"$ref":"#/components/schemas/UnreadableFile"},"type":"array"},"unresolvedReferences":{"items":{"$ref":"#/components/schemas/UnresolvedReference"},"type":"array"}},"required":["items","unreadable","agentDelegations","unresolvedReferences","agentAvailable","agentIdentities"],"type":"object"},"KeyItem":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"bits":{"type":"integer"},"certificate":{"$ref":"#/components/schemas/KeyCertificate"},"comment":{"type":"string"},"container":{"type":"string"},"encrypted":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"keyType":{"type":"string"},"kind":{"type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"permission":{"type":"string"},"permissionRisk":{"type":"boolean"},"references":{"items":{"$ref":"#/components/schemas/KeyReference"},"type":"array"},"relativePath":{"type":"string"}},"required":["id","relativePath","kind","container","algorithm","keyType","bits","encrypted","fingerprint","comment","permission","permissionRisk","references","notes"],"type":"object"},"KeyReference":{"additionalProperties":false,"properties":{"condition":{"type":"string"},"configPath":{"type":"string"},"directive":{"type":"string"},"hostPatterns":{"items":{"type":"string"},"type":"array"},"line":{"type":"integer"},"value":{"type":"string"}},"required":["directive","configPath","line","condition","hostPatterns","value"],"type":"object"},"KeyVariant":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"bits":{"type":"integer"},"inProcess":{"type":"boolean"},"label":{"type":"string"},"reason":{"type":"string"}},"required":["algorithm","bits","label","inProcess","reason"],"type":"object"},"KnownHostCandidate":{"additionalProperties":false,"properties":{"fingerprint":{"type":"string"},"host":{"type":"string"},"key":{"type":"string"},"keyType":{"type":"string"},"port":{"type":"integer"},"verified":{"type":"boolean"}},"required":["host","port","keyType","key","fingerprint","verified"],"type":"object"},"KnownHostEntry":{"additionalProperties":false,"properties":{"aliases":{"description":"The saved connections whose key this entry holds, matched by the name ssh looks the key up under (HostKeyAlias, or HostName and Port). A hashed entry is matched by hashing that name. Absent when the entry holds no saved connection's key.","items":{"type":"string"},"type":"array"},"comment":{"type":"string"},"digest":{"type":"string"},"fingerprint":{"type":"string"},"hashed":{"type":"boolean"},"hosts":{"items":{"type":"string"},"type":"array"},"keyType":{"type":"string"},"line":{"type":"integer"},"marker":{"type":"string"}},"required":["line","digest","marker","hosts","hashed","keyType","fingerprint","comment"],"type":"object"},"KnownHostTarget":{"additionalProperties":false,"properties":{"digest":{"maxLength":64,"minLength":64,"type":"string"},"line":{"type":"integer"}},"required":["line","digest"],"type":"object"},"KnownHostsAddRequest":{"additionalProperties":false,"properties":{"acknowledged":{"type":"boolean"},"expectedFingerprint":{"maxLength":128,"type":"string"},"host":{"maxLength":255,"minLength":1,"type":"string"},"key":{"maxLength":4096,"minLength":1,"type":"string"},"keyType":{"maxLength":64,"minLength":1,"type":"string"},"port":{"type":"integer"}},"required":["host","port","keyType","key","expectedFingerprint","acknowledged"],"type":"object"},"KnownHostsChangeResponse":{"additionalProperties":false,"properties":{"changed":{"type":"boolean"},"transactionId":{"type":"string"}},"required":["changed","transactionId"],"type":"object"},"KnownHostsDeleteRequest":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/KnownHostTarget"},"type":"array"}},"required":["entries"],"type":"object"},"KnownHostsResponse":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/KnownHostEntry"},"type":"array"},"path":{"type":"string"}},"required":["path","entries"],"type":"object"},"KnownHostsScanRequest":{"additionalProperties":false,"properties":{"host":{"maxLength":255,"minLength":1,"type":"string"},"port":{"type":"integer"}},"required":["host","port"],"type":"object"},"KnownHostsScanResponse":{"additionalProperties":false,"properties":{"candidates":{"items":{"$ref":"#/components/schemas/KnownHostCandidate"},"type":"array"},"notice":{"type":"string"}},"required":["notice","candidates"],"type":"object"},"L2TPProfile":{"additionalProperties":false,"properties":{"esp":{"maxLength":256,"type":"string"},"ike":{"maxLength":256,"type":"string"},"server":{"maxLength":320,"minLength":1,"type":"string"},"username":{"maxLength":256,"minLength":1,"type":"string"}},"required":["server","username"],"type":"object"},"LocalShellProfile":{"additionalProperties":false,"properties":{"arguments":{"items":{"type":"string"},"type":"array"},"default":{"type":"boolean"},"id":{"type":"string"},"label":{"type":"string"},"path":{"type":"string"}},"required":["id","label","path","arguments","default"],"type":"object"},"LocalShellProfileList":{"additionalProperties":false,"properties":{"profiles":{"items":{"$ref":"#/components/schemas/LocalShellProfile"},"type":"array"}},"required":["profiles"],"type":"object"},"Machine":{"additionalProperties":false,"properties":{"facts":{"$ref":"#/components/schemas/MachineFacts"},"id":{"pattern":"^[0-9a-f]{32}$","type":"string"},"settings":{"$ref":"#/components/schemas/MachineSettings"}},"required":["id","settings","facts"],"type":"object"},"MachineFacts":{"additionalProperties":false,"properties":{"os":{"type":"string"},"sessionLogDirectory":{"type":"string"},"startedAt":{"format":"date-time","type":"string"},"version":{"type":"string"}},"type":"object"},"MachineSettings":{"additionalProperties":false,"properties":{"name":{"maxLength":80,"type":"string"},"port":{"maximum":65535,"minimum":1024,"type":"integer"},"presetId":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"sessionLogDirectory":{"maxLength":4096,"type":"string"}},"type":"object"},"MachineState":{"additionalProperties":false,"properties":{"facts":{"$ref":"#/components/schemas/MachineFacts"},"interface":{"$ref":"#/components/schemas/InterfaceState"},"transfersStopped":{"type":"boolean"}},"required":["facts"],"type":"object"},"MachineStatus":{"additionalProperties":false,"properties":{"cpuPercent":{"maximum":100,"minimum":0,"type":"number"},"hostname":{"maxLength":255,"type":"string"},"memoryTotalBytes":{"format":"int64","minimum":0,"type":"integer"},"memoryUsedBytes":{"format":"int64","minimum":0,"type":"integer"},"os":{"maxLength":32,"minLength":1,"type":"string"},"uptimeSeconds":{"format":"int64","minimum":0,"type":"integer"}},"required":["hostname","os"],"type":"object"},"Metadata":{"additionalProperties":false,"properties":{"groups":{"items":{"$ref":"#/components/schemas/GroupMetadata"},"type":"array"},"groupsFile":{"type":"string"},"hosts":{"items":{"$ref":"#/components/schemas/HostMetadata"},"type":"array"},"keyBarPresets":{"items":{"$ref":"#/components/schemas/KeyBarPreset"},"maxItems":64,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"},"schemaVersion":{"type":"integer"},"settings":{"$ref":"#/components/schemas/GlobalSettings"},"shortcutPresets":{"items":{"$ref":"#/components/schemas/ShortcutPreset"},"maxItems":64,"type":"array"},"vpnProfiles":{"items":{"$ref":"#/components/schemas/VPNProfile"},"maxItems":64,"type":"array"}},"required":["schemaVersion","presets"],"type":"object"},"MoveTerminalSessionRequest":{"additionalProperties":false,"properties":{"workspaceId":{"maxLength":64,"minLength":1,"type":"string"}},"required":["workspaceId"],"type":"object"},"MoveTerminalWorkspaceRequest":{"additionalProperties":false,"properties":{"beforeId":{"maxLength":64,"minLength":1,"type":"string"}},"type":"object"},"Notice":{"additionalProperties":false,"properties":{"code":{"type":"string"},"detail":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"},"related":{"$ref":"#/components/schemas/NoticeLocation"}},"required":["code"],"type":"object"},"NoticeLocation":{"additionalProperties":false,"properties":{"line":{"type":"integer"},"path":{"type":"string"}},"required":["path","line"],"type":"object"},"NotificationSound":{"additionalProperties":false,"properties":{"sound":{"enum":["none","gentle","bell","pulse"],"type":"string"},"volume":{"maximum":100,"minimum":0,"type":"integer"}},"required":["sound","volume"],"type":"object"},"OpenConnectProfile":{"additionalProperties":false,"properties":{"approvalWord":{"maxLength":32,"type":"string"},"protocol":{"maxLength":32,"type":"string"},"secondFactor":{"maxLength":16,"type":"string"},"server":{"maxLength":320,"minLength":1,"type":"string"},"serverCertificate":{"maxLength":128,"type":"string"},"username":{"maxLength":256,"minLength":1,"type":"string"}},"required":["server","username"],"type":"object"},"OpenTerminalSessionRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"cols":{"maximum":1000,"minimum":1,"type":"integer"},"cwd":{"maxLength":4096,"minLength":1,"type":"string"},"destination":{"maxLength":519,"minLength":1,"type":"string"},"kind":{"enum":["ssh","shell"],"type":"string"},"profileId":{"pattern":"^[a-z0-9-]{1,64}$","type":"string"},"rows":{"maximum":1000,"minimum":1,"type":"integer"},"workspaceId":{"maxLength":64,"minLength":1,"type":"string"}},"required":["kind"],"type":"object"},"OpenTerminalSessionResponse":{"additionalProperties":false,"properties":{"session":{"$ref":"#/components/schemas/TerminalSession"},"streamTicket":{"type":"string"}},"required":["session","streamTicket"],"type":"object"},"OpenVPNProfile":{"additionalProperties":false,"properties":{"servers":{"items":{"maxLength":320,"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"},"username":{"maxLength":256,"type":"string"}},"required":["servers"],"type":"object"},"Overview":{"additionalProperties":false,"properties":{"diagnostics":{"items":{"$ref":"#/components/schemas/Diagnostic"},"type":"array"},"entry":{"$ref":"#/components/schemas/FileRef"},"files":{"items":{"$ref":"#/components/schemas/FileNode"},"type":"array"},"groups":{"items":{"$ref":"#/components/schemas/GroupView"},"type":"array"},"hosts":{"items":{"$ref":"#/components/schemas/HostEntry"},"type":"array"},"metadata":{"$ref":"#/components/schemas/Metadata"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"pending":{"items":{"$ref":"#/components/schemas/PendingTransaction"},"type":"array"}},"required":["entry","files","hosts","groups","metadata","diagnostics","notices"],"type":"object"},"PassphraseRequest":{"additionalProperties":false,"properties":{"passphrase":{"maxLength":1024,"type":"string"}},"required":["passphrase"],"type":"object"},"PasswordEligibility":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"blockers":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"hostName":{"type":"string"},"passwordBinding":{"enum":["unavailable","none","current","stale"],"type":"string"},"port":{"type":"string"},"storable":{"type":"boolean"},"totpBinding":{"enum":["unavailable","none","current","stale"],"type":"string"},"warnings":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"}},"required":["alias","storable","blockers","warnings"],"type":"object"},"PasswordVaultStatus":{"additionalProperties":false,"properties":{"aliases":{"items":{"type":"string"},"type":"array"},"dedicatedKeyPassphrases":{"items":{"type":"string"},"type":"array"},"exists":{"type":"boolean"},"migratedFromVersion":{"type":"integer"},"migratedToVersion":{"type":"integer"},"minPassphraseLength":{"type":"integer"},"passwordless":{"type":"boolean"},"unlocked":{"type":"boolean"}},"required":["exists","unlocked","aliases","dedicatedKeyPassphrases"],"type":"object"},"PendingChanges":{"additionalProperties":false,"properties":{"files":{"items":{"$ref":"#/components/schemas/PendingFileChange"},"type":"array"},"id":{"type":"string"}},"required":["id","files"],"type":"object"},"PendingFileChange":{"additionalProperties":false,"properties":{"action":{"enum":["write","move","remove","mkdir","rmdir","note"],"type":"string","x-go-type":"string"},"after":{"type":"string"},"afterExists":{"type":"boolean"},"before":{"type":"string"},"beforeExists":{"type":"boolean"},"committed":{"type":"boolean"},"contents":{"enum":["text","none","secret","binary","too_large","vault_locked","unavailable"],"type":"string","x-go-type":"string"},"path":{"type":"string"},"target":{"type":"string"}},"required":["path","action","committed","contents","beforeExists","afterExists"],"type":"object"},"PendingTransaction":{"additionalProperties":false,"properties":{"canComplete":{"type":"boolean"},"committed":{"type":"integer"},"id":{"type":"string"},"operation":{"type":"string"},"paths":{"items":{"type":"string"},"type":"array"},"startedAt":{"type":"string"},"status":{"type":"string"}},"required":["id","operation","status","startedAt","committed","paths","canComplete"],"type":"object"},"Problem":{"additionalProperties":false,"properties":{"blockers":{"items":{"type":"string"},"type":"array"},"code":{"type":"string"},"column":{"type":"integer"},"conflict":{"$ref":"#/components/schemas/ConflictReport"},"currentVersion":{"minimum":0,"type":"integer"},"detail":{"type":"string"},"diagnostics":{"items":{"$ref":"#/components/schemas/Diagnostic"},"type":"array"},"directive":{"type":"string"},"field":{"type":"string"},"limit":{"minimum":1,"type":"integer"},"line":{"type":"integer"},"message":{"type":"string"},"path":{"type":"string"},"reason":{"type":"string"},"requiredVersion":{"minimum":1,"type":"integer"}},"required":["code","message"],"type":"object"},"PublicKeyResponse":{"additionalProperties":false,"properties":{"comment":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"publicKey":{"type":"string"},"relativePath":{"type":"string"}},"required":["id","relativePath","publicKey","fingerprint","comment"],"type":"object"},"PullRequest":{"additionalProperties":false,"properties":{"acceptRemoteHead":{"type":"boolean"},"apply":{"type":"boolean"},"expectedETag":{"maxLength":1024,"type":"string"},"expectedRevision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"historyKey":{"maxLength":1024,"minLength":1,"type":"string"},"resolve":{"enum":["local","remote"],"type":"string"}},"type":"object"},"PullResponse":{"additionalProperties":false,"properties":{"added":{"items":{"type":"string"},"type":"array"},"applied":{"type":"boolean"},"completedAt":{"type":"string"},"conflicts":{"items":{"$ref":"#/components/schemas/SyncConflict"},"type":"array"},"downloadedBytes":{"format":"int64","minimum":0,"type":"integer"},"origin":{"type":"string"},"remoteETag":{"type":"string"},"remoteRevision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"removed":{"items":{"type":"string"},"type":"array"},"summary":{"$ref":"#/components/schemas/SnapshotSummary"},"written":{"items":{"type":"string"},"type":"array"}},"required":["applied","conflicts","written","added","removed","summary","downloadedBytes","completedAt","remoteETag","remoteRevision"],"type":"object"},"PurgeTrashResponse":{"additionalProperties":false,"properties":{"entryId":{"type":"string"},"removed":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["entryId","removed","transactionId"],"type":"object"},"PushResponse":{"additionalProperties":false,"properties":{"result":{"$ref":"#/components/schemas/PushResult"},"status":{"$ref":"#/components/schemas/SyncStatus"}},"required":["status","result"],"type":"object"},"PushResult":{"additionalProperties":false,"properties":{"added":{"items":{"type":"string"},"type":"array"},"completedAt":{"type":"string"},"modified":{"items":{"type":"string"},"type":"array"},"objectCount":{"minimum":0,"type":"integer"},"removed":{"items":{"type":"string"},"type":"array"},"summary":{"$ref":"#/components/schemas/SnapshotSummary"},"uploadedBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["summary","objectCount","uploadedBytes","completedAt","added","modified","removed"],"type":"object"},"ReachabilityResponse":{"additionalProperties":false,"properties":{"address":{"type":"string"},"detail":{"type":"string"},"notice":{"type":"string"},"outcome":{"type":"string"}},"required":["address","outcome","detail","notice"],"type":"object"},"RecentConnection":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hostName":{"type":"string"},"lastConnectedAt":{"format":"date-time","type":"string"},"port":{"type":"string"},"user":{"type":"string"}},"required":["alias","hostName","user","port","lastConnectedAt"],"type":"object"},"RecentConnectionList":{"additionalProperties":false,"properties":{"connections":{"items":{"$ref":"#/components/schemas/RecentConnection"},"maxItems":20,"type":"array"}},"required":["connections"],"type":"object"},"RecoverRequest":{"additionalProperties":false,"properties":{"action":{"maxLength":16,"minLength":1,"type":"string"},"transactionId":{"maxLength":128,"minLength":1,"type":"string"}},"required":["transactionId","action"],"type":"object"},"RecoverResponse":{"additionalProperties":false,"properties":{"status":{"enum":["ok"],"type":"string","x-go-type":"string"}},"required":["status"],"type":"object"},"RegisterKeyRequest":{"additionalProperties":false,"properties":{"lifetimeSeconds":{"type":"integer"},"passphrase":{"maxLength":1024,"type":"string"}},"required":["passphrase","lifetimeSeconds"],"type":"object"},"RegisterKeyResponse":{"additionalProperties":false,"properties":{"fingerprint":{"type":"string"},"id":{"type":"string"},"identities":{"items":{"$ref":"#/components/schemas/AgentIdentity"},"type":"array"},"lifetimeSeconds":{"type":"integer"},"relativePath":{"type":"string"}},"required":["id","relativePath","fingerprint","lifetimeSeconds","identities"],"type":"object"},"ReleaseNotes":{"additionalProperties":false,"properties":{"body":{"description":"Original Markdown displayed as plain text; bounded to 64 KiB of UTF-8.","maxLength":65536,"type":"string"},"pageUrl":{"type":"string"},"publishedAt":{"format":"date-time","type":"string"},"version":{"type":"string"}},"required":["version","pageUrl","body"],"type":"object"},"RelocateKeyRequest":{"additionalProperties":false,"properties":{"group":{"maxLength":400,"type":"string"},"newName":{"maxLength":64,"minLength":1,"type":"string"}},"type":"object"},"RelocateKeyResponse":{"additionalProperties":false,"properties":{"blockers":{"items":{"type":"string"},"type":"array"},"files":{"items":{"$ref":"#/components/schemas/RelocatedKeyFile"},"type":"array"},"group":{"type":"string"},"id":{"type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/components/schemas/RewrittenKeyReference"},"type":"array"},"relativePath":{"type":"string"},"skipped":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["id","relativePath","group","files","references","skipped","notes","blockers","transactionId"],"type":"object"},"RelocatedKeyFile":{"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"RemoteEngine":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"connected":{"type":"boolean"}},"required":["alias","connected"],"type":"object"},"RemoteEngineAction":{"enum":["stop","restart","update"],"type":"string","x-enum-varnames":["RemoteEngineActionStop","RemoteEngineActionRestart","RemoteEngineActionUpdate"]},"RemoteEngineActionLog":{"additionalProperties":false,"properties":{"action":{"$ref":"#/components/schemas/RemoteEngineAction"},"cursor":{"$ref":"#/components/schemas/TerminalControlCursor"},"done":{"type":"boolean"},"generation":{"format":"uint64","minimum":1,"type":"integer"},"output":{"maxLength":65536,"type":"string"},"problem":{"type":"string"},"succeeded":{"type":"boolean"}},"required":["generation","action","done","succeeded","cursor","output"],"type":"object"},"RemoteEngineActionRequest":{"additionalProperties":false,"properties":{"action":{"$ref":"#/components/schemas/RemoteEngineAction"}},"required":["action"],"type":"object"},"RemoteEngineActionStarted":{"additionalProperties":false,"properties":{"generation":{"format":"uint64","minimum":1,"type":"integer"}},"required":["generation"],"type":"object"},"RemoteEngineEntrance":{"additionalProperties":false,"properties":{"url":{"type":"string"}},"required":["url"],"type":"object"},"RemoteEngineList":{"additionalProperties":false,"properties":{"engines":{"items":{"$ref":"#/components/schemas/RemoteEngine"},"type":"array"}},"required":["engines"],"type":"object"},"RemoteEngineStatus":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"passwordless":{"type":"boolean"},"sessions":{"minimum":0,"type":"integer"},"unlocked":{"type":"boolean"},"vault":{"type":"boolean"},"version":{"type":"string"}},"required":["alias","version","vault","unlocked","passwordless","sessions"],"type":"object"},"RemoteKeyPlan":{"additionalProperties":false,"properties":{"actionToken":{"type":"string"},"alias":{"type":"string"},"executableDirectives":{"items":{"$ref":"#/components/schemas/ExecutableDirective"},"type":"array"},"fingerprint":{"type":"string"},"hostname":{"type":"string"},"keyLine":{"type":"string"},"keyPath":{"type":"string"},"manual":{"items":{"type":"string"},"type":"array"},"port":{"type":"string"},"remotePath":{"type":"string"},"routine":{"type":"string"},"supported":{"type":"boolean"},"user":{"type":"string"},"valuesFrom":{"type":"string"}},"required":["alias","user","hostname","port","valuesFrom","fingerprint","keyPath","keyLine","remotePath","routine","supported","manual","executableDirectives","actionToken"],"type":"object"},"RemoteKeyPlanRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"keyPath":{"maxLength":4096,"type":"string"},"publicKey":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["alias","keyPath","publicKey"],"type":"object"},"RemoteKeyRegisterRequest":{"additionalProperties":false,"properties":{"acknowledgeExecutable":{"type":"boolean"},"alias":{"maxLength":64,"minLength":1,"type":"string"},"keyPath":{"maxLength":4096,"type":"string"},"publicKey":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["alias","keyPath","publicKey","acknowledgeExecutable"],"type":"object"},"RemoteKeyRegisterResponse":{"additionalProperties":false,"properties":{"exitCode":{"type":"integer"},"outcome":{"type":"string"},"stderr":{"type":"string"},"truncated":{"type":"boolean"}},"required":["outcome","exitCode","stderr","truncated"],"type":"object"},"RenameTerminalBackgroundRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":128,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"ResetUnsupportedVaultRequest":{"additionalProperties":false,"properties":{"acknowledged":{"type":"boolean"},"passphrase":{"maxLength":1024,"type":"string"}},"required":["passphrase","acknowledged"],"type":"object"},"RestoreRequest":{"additionalProperties":false,"properties":{"path":{"maxLength":512,"minLength":1,"type":"string"},"transactionId":{"maxLength":128,"minLength":1,"type":"string"}},"required":["transactionId","path"],"type":"object"},"RestoreTrashResponse":{"additionalProperties":false,"properties":{"blockers":{"items":{"type":"string"},"type":"array"},"entryId":{"type":"string"},"restored":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["entryId","restored","blockers","transactionId"],"type":"object"},"RevealCredentialResponse":{"additionalProperties":false,"properties":{"kind":{"type":"string"},"name":{"type":"string"},"secret":{"type":"string"}},"required":["kind","name","secret"],"type":"object"},"RevealPrivateKeyResponse":{"additionalProperties":false,"properties":{"encrypted":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"privateKey":{"type":"string"},"relativePath":{"type":"string"},"transactionId":{"type":"string"}},"required":["id","relativePath","privateKey","encrypted","fingerprint","transactionId"],"type":"object"},"RewrittenKeyReference":{"additionalProperties":false,"properties":{"configPath":{"type":"string"},"directive":{"type":"string"},"from":{"type":"string"},"line":{"type":"integer"},"to":{"type":"string"}},"required":["directive","configPath","line","from","to"],"type":"object"},"SFTPChangeSymlinkRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"},"target":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","target","expectedRevision"],"type":"object"},"SFTPChmodEntry":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","expectedRevision"],"type":"object"},"SFTPChmodOptions":{"additionalProperties":false,"properties":{"directoryMode":{"pattern":"^0?[0-7]{3}$","type":"string"},"fileMode":{"pattern":"^0?[0-7]{3}$","type":"string"},"recursive":{"type":"boolean"}},"required":["fileMode","directoryMode","recursive"],"type":"object"},"SFTPChmodPlan":{"additionalProperties":false,"properties":{"actionToken":{"maxLength":43,"minLength":43,"type":"string"},"directories":{"minimum":0,"type":"integer"},"files":{"minimum":0,"type":"integer"},"options":{"$ref":"#/components/schemas/SFTPChmodOptions"},"revision":{"minLength":1,"type":"string"},"selectionCount":{"maximum":200,"minimum":1,"type":"integer"},"skippedSymlinks":{"minimum":0,"type":"integer"}},"required":["revision","selectionCount","files","directories","skippedSymlinks","options","actionToken"],"type":"object"},"SFTPChmodRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"mode":{"pattern":"^0?[0-7]{3}$","type":"string"},"path":{"type":"string"},"recursive":{"type":"boolean"}},"required":["path","mode","expectedRevision","recursive"],"type":"object"},"SFTPChmodResult":{"additionalProperties":false,"properties":{"applied":{"minimum":0,"type":"integer"},"complete":{"type":"boolean"},"items":{"minimum":1,"type":"integer"}},"required":["applied","items","complete"],"type":"object","x-sshc-less-than-or-equal":[["applied","items"]]},"SFTPChmodSelection":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPChmodEntry"},"maxItems":200,"minItems":1,"type":"array"},"options":{"$ref":"#/components/schemas/SFTPChmodOptions"}},"required":["entries","options"],"type":"object"},"SFTPChmodSelectionRequest":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPChmodEntry"},"maxItems":200,"minItems":1,"type":"array"},"expectedRevision":{"minLength":1,"type":"string"},"options":{"$ref":"#/components/schemas/SFTPChmodOptions"}},"required":["entries","options","expectedRevision"],"type":"object"},"SFTPComparisonMode":{"default":"metadata","enum":["metadata","content"],"type":"string"},"SFTPCompleteUploadRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"},"sourceFingerprint":{"pattern":"^tree-sha256:[0-9a-f]{64}$","type":"string"}},"required":["path","size","expectedRevision","sourceFingerprint"],"type":"object"},"SFTPConnectionAttempt":{"additionalProperties":false,"properties":{"finished":{"type":"boolean"},"hostKeyQuestion":{"$ref":"#/components/schemas/TerminalHostKeyQuestion"},"log":{"description":"Connection log lines at the configured verbosity, oldest first, without line endings. A failed attempt ends with the reason.","items":{"type":"string"},"type":"array"},"progress":{"$ref":"#/components/schemas/TerminalConnectionProgress"}},"required":["log","finished"],"type":"object"},"SFTPContentMatch":{"additionalProperties":false,"properties":{"entry":{"$ref":"#/components/schemas/SFTPEntry"},"line":{"minimum":1,"type":"integer"},"snippet":{"maxLength":162,"type":"string"}},"required":["entry","line","snippet"],"type":"object"},"SFTPCreateEntryRequest":{"additionalProperties":false,"properties":{"path":{"type":"string"},"type":{"enum":["directory","file"],"type":"string"}},"required":["path","type"],"type":"object"},"SFTPCreateSymlinkRequest":{"additionalProperties":false,"properties":{"path":{"maxLength":4096,"minLength":1,"type":"string"},"target":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","target"],"type":"object"},"SFTPCreateTransferJobRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"batchId":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"batchKind":{"enum":["file","folder"],"type":"string"},"batchName":{"maxLength":1024,"minLength":1,"type":"string"},"direction":{"enum":["upload","download","remote"],"type":"string"},"id":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"kind":{"enum":["file","folder"],"type":"string"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"lastModified":{"format":"int64","minimum":0,"type":"integer"},"name":{"maxLength":1024,"minLength":1,"type":"string"},"operation":{"enum":["","copy","move","delete","get","put"],"type":"string"},"overwrite":{"type":"boolean"},"remotePath":{"minLength":1,"type":"string"},"sourceAlias":{"maxLength":64,"type":"string"},"sourcePath":{"type":"string"},"totalBytes":{"format":"int64","minimum":-1,"type":"integer"}},"required":["id","batchId","batchName","batchKind","alias","sourceAlias","sourcePath","operation","overwrite","direction","kind","name","remotePath","totalBytes","lastModified"],"type":"object"},"SFTPDirectoryComparison":{"additionalProperties":false,"properties":{"bytesRead":{"format":"int64","maximum":268435456,"minimum":0,"type":"integer"},"entries":{"items":{"$ref":"#/components/schemas/SFTPDirectoryDifference"},"maxItems":20000,"type":"array"},"leftPath":{"type":"string"},"mode":{"$ref":"#/components/schemas/SFTPComparisonMode"},"rightPath":{"type":"string"},"truncated":{"type":"boolean"}},"required":["leftPath","rightPath","entries"],"type":"object"},"SFTPDirectoryDifference":{"additionalProperties":false,"properties":{"left":{"$ref":"#/components/schemas/SFTPEntry"},"omission":{"description":"byte_limit or unsupported; the content was not compared","type":"string"},"relativePath":{"type":"string"},"right":{"$ref":"#/components/schemas/SFTPEntry"},"status":{"enum":["same","different","left_only","right_only","type_mismatch","unverified"],"type":"string"}},"required":["relativePath","status"],"type":"object"},"SFTPDirectoryStats":{"additionalProperties":false,"properties":{"bytes":{"format":"int64","minimum":0,"type":"integer"},"directories":{"minimum":1,"type":"integer"},"files":{"minimum":0,"type":"integer"},"path":{"type":"string"},"truncated":{"type":"boolean"}},"required":["path","bytes","files","directories","truncated"],"type":"object"},"SFTPDownloadCheckpointRequest":{"additionalProperties":false,"properties":{"offset":{"format":"int64","minimum":0,"type":"integer"},"revision":{"maxLength":256,"minLength":1,"type":"string"}},"required":["offset","revision"],"type":"object"},"SFTPDownloadPartProgress":{"additionalProperties":false,"properties":{"index":{"maximum":127,"minimum":0,"type":"integer"},"totalBytes":{"format":"int64","minimum":0,"type":"integer"},"transferredBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["index","transferredBytes","totalBytes"],"type":"object","x-sshc-less-than-or-equal":[["transferredBytes","totalBytes"]]},"SFTPEntry":{"additionalProperties":false,"properties":{"gid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer"},"linkTarget":{"type":"string"},"mode":{"type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"path":{"type":"string"},"revision":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"},"targetType":{"enum":["file","directory","other"],"type":"string"},"type":{"enum":["file","directory","symlink","other"],"type":"string"},"uid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer"}},"required":["name","path","type","size","mode","modifiedAt","revision"],"type":"object"},"SFTPFilesystemSpace":{"additionalProperties":false,"properties":{"availableBytes":{"description":"Bytes available to the authenticated account as an exact uint64 decimal string.","pattern":"^(0|[1-9][0-9]{0,19})$","type":"string"},"path":{"type":"string"},"totalBytes":{"description":"Total filesystem bytes as an exact uint64 decimal string.","pattern":"^(0|[1-9][0-9]{0,19})$","type":"string"}},"required":["path","availableBytes","totalBytes"],"type":"object"},"SFTPListing":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPEntry"},"type":"array"},"path":{"type":"string"}},"required":["path","entries"],"type":"object"},"SFTPLocalDeleteEntry":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","expectedRevision"],"type":"object"},"SFTPLocalDeletePlan":{"additionalProperties":false,"properties":{"actionToken":{"minLength":1,"type":"string"},"items":{"format":"int64","minimum":1,"type":"integer"},"revision":{"minLength":1,"type":"string"}},"required":["revision","items","actionToken"],"type":"object"},"SFTPLocalDeleteRequest":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPLocalDeleteEntry"},"maxItems":200,"minItems":1,"type":"array"},"expectedRevision":{"minLength":1,"type":"string"}},"required":["entries","expectedRevision"],"type":"object"},"SFTPLocalDeleteSelection":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPLocalDeleteEntry"},"maxItems":200,"minItems":1,"type":"array"}},"required":["entries"],"type":"object"},"SFTPLocalListing":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPEntry"},"type":"array"},"home":{"type":"string"},"path":{"type":"string"}},"required":["path","home","entries"],"type":"object"},"SFTPLocalMkdirRequest":{"additionalProperties":false,"properties":{"directory":{"maxLength":4096,"minLength":1,"type":"string"},"name":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["directory","name"],"type":"object"},"SFTPLocalRenameRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"name":{"maxLength":1024,"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","name","expectedRevision"],"type":"object"},"SFTPMediaTicket":{"additionalProperties":false,"properties":{"ticket":{"minLength":1,"type":"string"}},"required":["ticket"],"type":"object"},"SFTPMediaTicketRequest":{"additionalProperties":false,"properties":{"alias":{"description":"An SSH alias, or sshc://local for the engine's own disk","minLength":1,"type":"string"},"path":{"minLength":1,"type":"string"}},"required":["alias","path"],"type":"object"},"SFTPOwnershipRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"gid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer","x-go-type":"*int64"},"path":{"maxLength":4096,"minLength":1,"type":"string"},"uid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer","x-go-type":"*int64"}},"required":["path","uid","gid","expectedRevision"],"type":"object"},"SFTPRenameRequest":{"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"SFTPResumableUpload":{"additionalProperties":false,"properties":{"chunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"completedRanges":{"items":{"$ref":"#/components/schemas/SFTPUploadRange"},"maxItems":65536,"type":"array"},"expectedRevision":{"type":"string"},"id":{"type":"string"},"offset":{"format":"int64","minimum":0,"type":"integer"},"parallelism":{"maximum":128,"minimum":1,"type":"integer"},"path":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"}},"required":["id","path","offset","size","expectedRevision","completedRanges","parallelism","chunkBytes"],"type":"object"},"SFTPSaveTextRequest":{"additionalProperties":false,"properties":{"contents":{"maxLength":2097152,"type":"string"},"expectedRevision":{"minLength":1,"type":"string"}},"required":["contents","expectedRevision"],"type":"object"},"SFTPSearchOmission":{"additionalProperties":false,"properties":{"count":{"minimum":1,"type":"integer"},"reason":{"description":"symlink, unsupported, binary, file_size, unreadable, changed, byte_limit, result_limit, entry_limit or depth_limit","type":"string"}},"required":["reason","count"],"type":"object"},"SFTPSearchResult":{"additionalProperties":false,"properties":{"bytesRead":{"format":"int64","maximum":67108864,"minimum":0,"type":"integer"},"entries":{"items":{"$ref":"#/components/schemas/SFTPEntry"},"maxItems":200,"type":"array"},"matches":{"items":{"$ref":"#/components/schemas/SFTPContentMatch"},"maxItems":200,"type":"array"},"omissions":{"items":{"$ref":"#/components/schemas/SFTPSearchOmission"},"maxItems":10,"type":"array"},"path":{"type":"string"},"query":{"type":"string"},"truncated":{"type":"boolean"}},"required":["path","query","truncated","entries"],"type":"object"},"SFTPStartUploadRequest":{"additionalProperties":false,"properties":{"path":{"type":"string"},"size":{"format":"int64","maximum":549755813888,"minimum":0,"type":"integer"},"sourceFingerprint":{"pattern":"^tree-sha256:[0-9a-f]{64}$","type":"string"}},"required":["path","size","sourceFingerprint"],"type":"object"},"SFTPTextFile":{"additionalProperties":false,"properties":{"contents":{"maxLength":2097152,"type":"string"},"entry":{"$ref":"#/components/schemas/SFTPEntry"},"revision":{"type":"string"}},"required":["entry","contents","revision"],"type":"object"},"SFTPTransfer":{"additionalProperties":false,"properties":{"bytes":{"format":"int64","minimum":0,"type":"integer"},"path":{"type":"string"},"revision":{"type":"string"}},"required":["path","bytes","revision"],"type":"object"},"SFTPTransferJob":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"allowedActions":{"items":{"enum":["pause","resume","retry","cancel","remove"],"type":"string"},"maxItems":3,"type":"array","uniqueItems":true},"attempt":{"minimum":1,"type":"integer"},"batchId":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"batchKind":{"enum":["file","folder"],"type":"string"},"batchName":{"maxLength":1024,"minLength":1,"type":"string"},"bytesPerSecond":{"format":"double","minimum":0,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"direction":{"enum":["upload","download","remote"],"type":"string"},"downloadParts":{"items":{"$ref":"#/components/schemas/SFTPDownloadPartProgress"},"maxItems":128,"type":"array"},"downloadRevision":{"maxLength":256,"type":"string"},"excludePatterns":{"description":"Exclusions captured when this folder job was queued.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"expectedRevision":{"maxLength":256,"type":"string"},"id":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"kind":{"enum":["file","folder"],"type":"string"},"lastModified":{"format":"int64","minimum":0,"type":"integer"},"name":{"maxLength":1024,"minLength":1,"type":"string"},"operation":{"enum":["","copy","move","delete","get","put"],"type":"string"},"overwrite":{"type":"boolean"},"problem":{"maxLength":128,"type":"string"},"reconnectAt":{"description":"Next reconnect time, or empty while not waiting.","type":"string"},"reconnectAttempt":{"maximum":10,"minimum":0,"type":"integer"},"remainingSeconds":{"format":"int64","minimum":-1,"type":"integer"},"remotePath":{"minLength":1,"type":"string"},"sourceAlias":{"maxLength":64,"type":"string"},"sourceFingerprint":{"maxLength":128,"type":"string"},"sourcePath":{"type":"string"},"status":{"enum":["queued","running","reconnecting","paused","reattach","needs_overwrite","completed","failed","cancelled"],"type":"string"},"totalBytes":{"format":"int64","minimum":-1,"type":"integer"},"transferredBytes":{"format":"int64","minimum":0,"type":"integer"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","batchId","batchName","batchKind","alias","sourceAlias","sourcePath","operation","direction","kind","name","remotePath","totalBytes","transferredBytes","bytesPerSecond","remainingSeconds","status","allowedActions","attempt","reconnectAttempt","reconnectAt","problem","lastModified","expectedRevision","sourceFingerprint","overwrite","downloadRevision","downloadParts","createdAt","updatedAt"],"type":"object"},"SFTPTransferJobActionRequest":{"additionalProperties":false,"properties":{"action":{"enum":["start","reconnect","pause","resume","retry","cancel","progress","complete","fail","needs_overwrite"],"type":"string"},"problem":{"maxLength":128,"type":"string"},"resetProgress":{"type":"boolean"},"totalBytes":{"format":"int64","minimum":0,"type":"integer"},"transferredBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["action"],"type":"object"},"SFTPTransferJobList":{"additionalProperties":false,"properties":{"autoReconnect":{"type":"boolean"},"clearCompletedAfterSeconds":{"maximum":86400,"minimum":0,"type":"integer"},"excludePatterns":{"description":"Folder contents to exclude. Basenames match at any depth; patterns containing / start at the selected folder. Supports * and ? only. Defaults to no exclusions.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"jobs":{"items":{"$ref":"#/components/schemas/SFTPTransferJob"},"maxItems":200,"type":"array"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"maxConcurrent":{"maximum":8,"minimum":1,"type":"integer"},"maxReconnectAttempts":{"maximum":10,"minimum":0,"type":"integer"},"processingStopped":{"type":"boolean"},"speedLimitBytesPerSecond":{"format":"int64","maximum":1073741824,"minimum":0,"type":"integer"}},"required":["maxConcurrent","clearCompletedAfterSeconds","processingStopped","largeFileThresholdBytes","largeFileParallelism","largeFileChunkBytes","speedLimitBytesPerSecond","autoReconnect","maxReconnectAttempts","jobs"],"type":"object"},"SFTPTransferQueueMoveRequest":{"additionalProperties":false,"properties":{"move":{"enum":["up","down","top","bottom"],"type":"string"}},"required":["move"],"type":"object"},"SFTPTransferSettingsRequest":{"additionalProperties":false,"properties":{"autoReconnect":{"type":"boolean"},"clearCompletedAfterSeconds":{"maximum":86400,"minimum":0,"type":"integer"},"excludePatterns":{"description":"Folder contents to exclude. Basenames match at any depth; patterns containing / start at the selected folder. Supports * and ? only. Defaults to no exclusions.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"maxConcurrent":{"maximum":8,"minimum":1,"type":"integer"},"maxReconnectAttempts":{"maximum":10,"minimum":0,"type":"integer"},"processingStopped":{"type":"boolean"},"speedLimitBytesPerSecond":{"format":"int64","maximum":1073741824,"minimum":0,"type":"integer"}},"required":["maxConcurrent","clearCompletedAfterSeconds","processingStopped","largeFileThresholdBytes","largeFileParallelism","largeFileChunkBytes","speedLimitBytesPerSecond","autoReconnect","maxReconnectAttempts"],"type":"object"},"SFTPUploadRange":{"additionalProperties":false,"properties":{"offset":{"format":"int64","minimum":0,"type":"integer"},"size":{"format":"int64","maximum":4294967296,"minimum":1,"type":"integer"}},"required":["offset","size"],"type":"object"},"SavePreview":{"additionalProperties":false,"properties":{"diffs":{"items":{"$ref":"#/components/schemas/FileDiff"},"type":"array"},"effective":{"items":{"$ref":"#/components/schemas/EffectiveDiff"},"type":"array"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"operation":{"type":"string"}},"required":["operation","diffs"],"type":"object"},"SaveResult":{"additionalProperties":false,"properties":{"preview":{"$ref":"#/components/schemas/SavePreview"},"transactionId":{"type":"string"},"written":{"items":{"type":"string"},"type":"array"}},"required":["transactionId","written","preview"],"type":"object"},"SetTerminalSessionTitleRequest":{"additionalProperties":false,"properties":{"title":{"maxLength":64,"minLength":1,"nullable":true,"type":"string"}},"required":["title"],"type":"object"},"Setting":{"additionalProperties":false,"properties":{"keyword":{"type":"string"},"values":{"description":"The arguments of the directive. ProxyCommand, RemoteCommand, LocalCommand and KnownHostsCommand take the rest of the line as one value, written without requoting; a request with more than one value for them is refused.","items":{"type":"string"},"type":"array"}},"required":["keyword","values"],"type":"object"},"SettingsDocument":{"additionalProperties":false,"properties":{"global":{"$ref":"#/components/schemas/GlobalSettings"},"machineId":{"type":"string"},"machines":{"items":{"$ref":"#/components/schemas/Machine"},"type":"array"},"presetInUse":{"type":"string"},"presets":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"},"state":{"$ref":"#/components/schemas/MachineState"}},"required":["machineId","global","presets","presetInUse","machines","state"],"type":"object"},"SettingsPreset":{"additionalProperties":false,"properties":{"appearance":{"$ref":"#/components/schemas/AppearanceSettings"},"fileTransfers":{"$ref":"#/components/schemas/FileTransferSettings"},"id":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"interface":{"$ref":"#/components/schemas/InterfaceSettings"},"name":{"maxLength":80,"minLength":1,"type":"string"},"terminal":{"$ref":"#/components/schemas/EmbeddedTerminal"},"vaultAutoLock":{"$ref":"#/components/schemas/VaultAutoLockSettings"}},"required":["id"],"type":"object"},"SettingsSaveResult":{"additionalProperties":false,"properties":{"transactionId":{"type":"string"},"written":{"items":{"type":"string"},"type":"array"}},"required":["transactionId","written"],"type":"object"},"ShortcutPreset":{"additionalProperties":false,"properties":{"bindings":{"additionalProperties":false,"properties":{"closeSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"copy":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"decreaseFontSize":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"home":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"increaseFontSize":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"newSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"nextPane":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"nextPrompt":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"nextSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"palette":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"paste":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"previousPane":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"previousPrompt":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"previousSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"quickCommands":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"resetFontSize":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"sftp":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"splitBelow":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"splitRight":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"terminalSearch":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"toggleNavigation":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"}},"required":["palette","terminalSearch","copy","paste","previousPrompt","nextPrompt","increaseFontSize","decreaseFontSize","resetFontSize","quickCommands","newSession","closeSession","nextSession","previousSession","nextPane","previousPane","splitRight","splitBelow","toggleNavigation","home","sftp"],"type":"object"},"id":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"name":{"maxLength":80,"minLength":1,"type":"string"}},"required":["id","name","bindings"],"type":"object"},"SnapshotSummary":{"additionalProperties":false,"properties":{"createdAt":{"type":"string"},"fileCount":{"minimum":0,"type":"integer"},"snapshotBytes":{"format":"int64","minimum":0,"type":"integer"},"sourceBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["createdAt","fileCount","sourceBytes","snapshotBytes"],"type":"object"},"Snippet":{"additionalProperties":false,"properties":{"command":{"description":"The command, or empty for a snippet of steps.","maxLength":65536,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"description":{"maxLength":2048,"type":"string"},"id":{"type":"string"},"name":{"maxLength":96,"minLength":1,"type":"string"},"steps":{"items":{"$ref":"#/components/schemas/SnippetStep"},"maxItems":32,"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"variables":{"items":{"$ref":"#/components/schemas/SnippetVariable"},"maxItems":32,"type":"array"},"waitSeconds":{"maximum":300,"minimum":0,"type":"integer"}},"required":["name","description","command","variables","id","createdAt","updatedAt"],"type":"object"},"SnippetDraft":{"additionalProperties":false,"properties":{"command":{"description":"The command, or empty for a snippet of steps.","maxLength":65536,"type":"string"},"description":{"maxLength":2048,"type":"string"},"name":{"maxLength":96,"minLength":1,"type":"string"},"steps":{"items":{"$ref":"#/components/schemas/SnippetStep"},"maxItems":32,"type":"array"},"variables":{"items":{"$ref":"#/components/schemas/SnippetVariable"},"maxItems":32,"type":"array"},"waitSeconds":{"description":"How long each step waits for its text; 0 is the default of 10 seconds.","maximum":300,"minimum":0,"type":"integer"}},"required":["name","description","command","variables"],"type":"object"},"SnippetExecuteRequest":{"additionalProperties":false,"description":"Exactly one of snippetId or command and exactly one of aliases or targets must be supplied. targets permits repeated aliases when targetId values are distinct.","properties":{"aliases":{"items":{"type":"string"},"maxItems":64,"minItems":1,"type":"array"},"command":{"maxLength":65536,"minLength":1,"type":"string"},"concurrency":{"maximum":8,"minimum":1,"type":"integer"},"evidence":{"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/SnippetExecutionTarget"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs","evidence","concurrency"],"type":"object","x-sshc-exactly-one":[["snippetId","command"],["aliases","targets"]]},"SnippetExecutionTarget":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"targetId":{"maxLength":255,"minLength":1,"type":"string"}},"required":["targetId","alias"],"type":"object"},"SnippetJob":{"additionalProperties":false,"properties":{"finishedAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"results":{"items":{"$ref":"#/components/schemas/SnippetTargetResult"},"type":"array"},"startedAt":{"format":"date-time","type":"string"},"status":{"enum":["running","completed","cancelled"],"type":"string"}},"required":["id","status","startedAt","results"],"type":"object"},"SnippetLibrary":{"additionalProperties":false,"properties":{"snippets":{"items":{"$ref":"#/components/schemas/Snippet"},"type":"array"},"startup":{"items":{"$ref":"#/components/schemas/StartupSnippet"},"type":"array"}},"required":["snippets","startup"],"type":"object"},"SnippetPreview":{"additionalProperties":false,"properties":{"actionExpiresAt":{"format":"date-time","type":"string"},"actionToken":{"type":"string"},"evidence":{"type":"string"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/SnippetPreviewTarget"},"type":"array"}},"required":["snippetId","evidence","targets","actionToken","actionExpiresAt"],"type":"object"},"SnippetPreviewRequest":{"additionalProperties":false,"description":"Exactly one of snippetId or command and exactly one of aliases or targets must be supplied. targets permits repeated aliases when targetId values are distinct.","properties":{"aliases":{"items":{"type":"string"},"maxItems":64,"minItems":1,"type":"array"},"command":{"maxLength":65536,"minLength":1,"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/SnippetExecutionTarget"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs"],"type":"object","x-sshc-exactly-one":[["snippetId","command"],["aliases","targets"]]},"SnippetPreviewTarget":{"additionalProperties":false,"properties":{"command":{"type":"string"},"target":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hostName":{"type":"string"},"port":{"type":"string"},"route":{"items":{"properties":{"alias":{"type":"string"},"authentication":{"items":{"type":"string"},"type":"array"},"hostKeyAlgorithms":{"items":{"type":"string"},"type":"array"},"hostName":{"type":"string"},"identitiesOnly":{"type":"boolean"},"identityFiles":{"items":{"type":"string"},"type":"array"},"port":{"type":"string"},"proxyCommand":{"type":"string"},"strictHostKey":{"type":"string"},"user":{"type":"string"}},"required":["alias","hostName","user","port","strictHostKey","authentication"],"type":"object"},"type":"array"},"user":{"type":"string"}},"required":["alias","hostName","user","port"],"type":"object"},"targetId":{"type":"string"}},"required":["targetId","target","command"],"type":"object"},"SnippetStep":{"additionalProperties":false,"description":"One step of a snippet of steps. It waits for wait in the shell's output when wait is given, then types send, with Enter after it when enter is true.","properties":{"enter":{"type":"boolean"},"optional":{"description":"A step whose text does not come in time is passed over instead of stopping the steps.","type":"boolean"},"send":{"maxLength":4095,"type":"string"},"wait":{"maxLength":256,"type":"string"}},"required":["send","enter"],"type":"object"},"SnippetTargetResult":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"exitCode":{"type":"integer"},"problem":{"type":"string"},"status":{"enum":["queued","running","succeeded","failed","cancelled"],"type":"string"},"stderr":{"type":"string"},"stdout":{"type":"string"},"targetId":{"type":"string"},"truncated":{"type":"boolean"}},"required":["targetId","alias","status"],"type":"object"},"SnippetVariable":{"additionalProperties":false,"properties":{"default":{"type":"string"},"description":{"type":"string"},"name":{"type":"string"},"required":{"type":"boolean"},"type":{"enum":["string","integer","boolean","secret"],"type":"string"}},"required":["name","type"],"type":"object"},"Source":{"additionalProperties":false,"properties":{"absolute":{"type":"string"},"condition":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"}},"type":"object"},"StartTerminalForwardRequest":{"additionalProperties":false,"properties":{"destination":{"maxLength":512,"type":"string"},"kind":{"pattern":"^(local|remote|dynamic)$","type":"string"},"listenPort":{"maximum":65535,"minimum":0,"type":"integer"}},"required":["kind","listenPort"],"type":"object"},"StartupSnippet":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"snippetId":{"type":"string"},"stale":{"description":"The host's destination changed after the assignment, so the snippet is not sent until it is assigned again.","type":"boolean"}},"required":["alias","snippetId","stale"],"type":"object"},"StartupSnippetRequest":{"additionalProperties":false,"properties":{"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"}},"required":["snippetId","inputs"],"type":"object"},"StoreCredentialRequest":{"additionalProperties":false,"properties":{"secret":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["secret"],"type":"object"},"SyncBucketObject":{"additionalProperties":false,"properties":{"key":{"minLength":1,"type":"string"},"lastModified":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"}},"required":["key","size"],"type":"object"},"SyncBucketStatus":{"additionalProperties":false,"properties":{"checkedAt":{"type":"string"},"history":{"items":{"$ref":"#/components/schemas/SyncBucketObject"},"maxItems":10000,"type":"array"},"historyTruncated":{"type":"boolean"},"live":{"$ref":"#/components/schemas/SyncBucketObject"},"localIsLive":{"type":"boolean"}},"required":["checkedAt","localIsLive","history","historyTruncated"],"type":"object"},"SyncConflict":{"additionalProperties":false,"properties":{"baseMode":{"pattern":"^0[67]00$","type":"string"},"changedHere":{"type":"boolean"},"changedThere":{"type":"boolean"},"localMode":{"pattern":"^0[67]00$","type":"string"},"path":{"type":"string"},"remoteMode":{"pattern":"^0[67]00$","type":"string"}},"required":["path","changedHere","changedThere"],"type":"object"},"SyncDirection":{"enum":["both","push","pull"],"type":"string"},"SyncExclusionCandidate":{"additionalProperties":false,"properties":{"ignored":{"type":"boolean"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","ignored"],"type":"object"},"SyncExclusions":{"additionalProperties":false,"properties":{"candidates":{"items":{"$ref":"#/components/schemas/SyncExclusionCandidate"},"maxItems":4096,"type":"array"},"document":{"maxLength":32768,"type":"string"},"usingDefaults":{"type":"boolean"}},"required":["document","usingDefaults","candidates"],"type":"object"},"SyncExclusionsRequest":{"additionalProperties":false,"properties":{"document":{"maxLength":32768,"type":"string"}},"required":["document"],"type":"object"},"SyncHistory":{"additionalProperties":false,"properties":{"checkedAt":{"type":"string"},"downloadTruncated":{"type":"boolean"},"downloadedBytes":{"format":"int64","maximum":134217728,"minimum":0,"type":"integer"},"historyTruncated":{"type":"boolean"},"revisions":{"items":{"$ref":"#/components/schemas/SyncHistoryRevision"},"maxItems":50,"type":"array"},"skipped":{"maximum":50,"minimum":0,"type":"integer"}},"required":["checkedAt","revisions","historyTruncated","downloadTruncated","downloadedBytes","skipped"],"type":"object"},"SyncHistoryDiff":{"additionalProperties":false,"properties":{"added":{"items":{"type":"string"},"type":"array"},"downloadedBytes":{"format":"int64","minimum":0,"type":"integer"},"modified":{"items":{"type":"string"},"type":"array"},"removed":{"items":{"type":"string"},"type":"array"}},"required":["added","modified","removed","downloadedBytes"],"type":"object"},"SyncHistoryDiffRequest":{"additionalProperties":false,"properties":{"key":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"SyncHistoryRelation":{"enum":["head","ancestor","branch"],"type":"string"},"SyncHistoryRevision":{"additionalProperties":false,"properties":{"createdAt":{"type":"string"},"fileCount":{"maximum":4096,"minimum":0,"type":"integer"},"key":{"maxLength":1024,"minLength":1,"type":"string"},"lastModified":{"type":"string"},"message":{"maxLength":240,"minLength":1,"type":"string"},"origin":{"type":"string"},"parentRevision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"relation":{"$ref":"#/components/schemas/SyncHistoryRelation"},"revision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"}},"required":["key","revision","message","createdAt","origin","fileCount","size","relation"],"type":"object"},"SyncKeyRequest":{"additionalProperties":false,"properties":{"confirmHistoryLoss":{"type":"boolean"},"key":{"maxLength":1024,"minLength":1,"type":"string"}},"type":"object"},"SyncKeyResponse":{"additionalProperties":false,"properties":{"key":{"type":"string"}},"required":["key"],"type":"object"},"SyncOperation":{"additionalProperties":false,"properties":{"completedAt":{"type":"string"},"downloadedBytes":{"format":"int64","minimum":0,"type":"integer"},"kind":{"$ref":"#/components/schemas/SyncOperationKind"},"objectCount":{"minimum":0,"type":"integer"},"removed":{"minimum":0,"type":"integer"},"summary":{"$ref":"#/components/schemas/SnapshotSummary"},"uploadedBytes":{"format":"int64","minimum":0,"type":"integer"},"written":{"minimum":0,"type":"integer"}},"required":["kind","summary","completedAt"],"type":"object"},"SyncOperationKind":{"enum":["push","apply"],"type":"string"},"SyncPushDraft":{"additionalProperties":false,"properties":{"added":{"maximum":4096,"minimum":0,"type":"integer"},"message":{"maxLength":240,"minLength":1,"type":"string"},"modified":{"maximum":4096,"minimum":0,"type":"integer"},"removed":{"maximum":4096,"minimum":0,"type":"integer"}},"required":["message","added","modified","removed"],"type":"object"},"SyncPushRequest":{"additionalProperties":false,"properties":{"message":{"maxLength":240,"minLength":1,"type":"string"}},"required":["message"],"type":"object"},"SyncSetupCheckRequest":{"additionalProperties":false,"properties":{"accessKeyId":{"maxLength":512,"minLength":1,"type":"string"},"bucket":{"maxLength":255,"minLength":1,"type":"string"},"endpoint":{"maxLength":2048,"minLength":1,"type":"string"},"path":{"maxLength":255,"type":"string"},"region":{"maxLength":64,"type":"string"},"reuseCredentials":{"type":"boolean"},"secretAccessKey":{"maxLength":512,"minLength":1,"type":"string"}},"required":["endpoint","bucket","reuseCredentials"],"type":"object"},"SyncSetupCheckResponse":{"additionalProperties":false,"properties":{"checkedAt":{"type":"string"},"etag":{"maxLength":1024,"minLength":1,"type":"string"},"historyPresent":{"type":"boolean"},"state":{"$ref":"#/components/schemas/SyncSetupTargetState"}},"required":["state","historyPresent","checkedAt"],"type":"object"},"SyncSetupRequest":{"additionalProperties":false,"properties":{"accessKeyId":{"maxLength":512,"minLength":1,"type":"string"},"bucket":{"maxLength":255,"minLength":1,"type":"string"},"direction":{"$ref":"#/components/schemas/SyncDirection"},"endpoint":{"maxLength":2048,"minLength":1,"type":"string"},"expectedETag":{"maxLength":1024,"minLength":1,"type":"string"},"expectedState":{"$ref":"#/components/schemas/SyncSetupTargetState"},"historyPresent":{"type":"boolean"},"key":{"maxLength":1024,"type":"string"},"path":{"maxLength":255,"type":"string"},"region":{"maxLength":64,"type":"string"},"reuseCredentials":{"type":"boolean"},"reuseKey":{"type":"boolean"},"secretAccessKey":{"maxLength":512,"minLength":1,"type":"string"}},"required":["endpoint","bucket","direction","expectedState","historyPresent","key","reuseCredentials","reuseKey"],"type":"object"},"SyncSetupResponse":{"additionalProperties":false,"properties":{"generatedKey":{"minLength":1,"type":"string"},"status":{"$ref":"#/components/schemas/SyncStatus"}},"required":["status"],"type":"object"},"SyncSetupTargetState":{"enum":["empty","existing","incomplete"],"type":"string"},"SyncStatus":{"additionalProperties":false,"properties":{"accessKeySuffix":{"maxLength":5,"type":"string"},"auto":{"$ref":"#/components/schemas/AutoSync"},"bucket":{"type":"string"},"configured":{"type":"boolean"},"direction":{"$ref":"#/components/schemas/SyncDirection"},"endpoint":{"type":"string"},"fileCount":{"type":"integer"},"keyConfigured":{"type":"boolean"},"lastOperation":{"$ref":"#/components/schemas/SyncOperation"},"lastSyncedAt":{"type":"string"},"locked":{"type":"boolean"},"origin":{"type":"string"},"path":{"type":"string"},"region":{"type":"string"},"synced":{"type":"boolean"}},"required":["configured","locked","endpoint","bucket","synced","direction","keyConfigured","auto"],"type":"object"},"TOTPCodeSet":{"additionalProperties":false,"properties":{"current":{"pattern":"^[0-9]{6,8}$","type":"string"},"next":{"pattern":"^[0-9]{6,8}$","type":"string"},"periodSeconds":{"maximum":300,"minimum":5,"type":"integer"},"previous":{"pattern":"^[0-9]{6,8}$","type":"string"},"remainingSeconds":{"maximum":300,"minimum":1,"type":"integer"}},"required":["previous","current","next","periodSeconds","remainingSeconds"],"type":"object"},"TerminalAppearance":{"additionalProperties":false,"properties":{"background":{"maxLength":128,"type":"string"},"backgroundTint":{"maximum":100,"minimum":0,"type":"integer"},"font":{"maxLength":64,"type":"string"},"palette":{"maxLength":64,"type":"string"}},"type":"object"},"TerminalBackground":{"additionalProperties":false,"properties":{"bytes":{"type":"integer"},"name":{"type":"string"},"type":{"type":"string"}},"required":["name","bytes","type"],"type":"object"},"TerminalBackgroundCapacityRequest":{"additionalProperties":false,"properties":{"capacityMiB":{"maximum":1024,"minimum":1,"type":"integer"}},"required":["capacityMiB"],"type":"object"},"TerminalBackgroundList":{"additionalProperties":false,"properties":{"backgrounds":{"items":{"$ref":"#/components/schemas/TerminalBackground"},"type":"array"},"capacityBytes":{"type":"integer"},"remainingBytes":{"type":"integer"},"usedBytes":{"type":"integer"}},"required":["backgrounds","usedBytes","capacityBytes","remainingBytes"],"type":"object"},"TerminalCommandDispatchRequest":{"additionalProperties":false,"properties":{"command":{"maxLength":4095,"minLength":1,"type":"string"},"evidence":{"maxLength":64,"minLength":64,"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"},"submit":{"description":"Send Enter after the command. Defaults to true.","type":"boolean"},"targets":{"items":{"$ref":"#/components/schemas/TerminalCommandTargetRequest"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs","targets","evidence"],"type":"object"},"TerminalCommandDispatchResponse":{"additionalProperties":false,"properties":{"results":{"items":{"$ref":"#/components/schemas/TerminalCommandResult"},"type":"array"}},"required":["results"],"type":"object"},"TerminalCommandPreview":{"additionalProperties":false,"properties":{"actionExpiresAt":{"type":"string"},"actionToken":{"type":"string"},"evidence":{"type":"string"},"reviewEvidence":{"description":"Stable evidence for the displayed redacted command and exact targets, independent of whether Enter will be sent.","type":"string"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/TerminalCommandPreviewTarget"},"type":"array"}},"required":["snippetId","evidence","reviewEvidence","actionToken","actionExpiresAt","targets"],"type":"object"},"TerminalCommandPreviewRequest":{"additionalProperties":false,"properties":{"command":{"maxLength":4095,"minLength":1,"type":"string"},"expectedReviewEvidence":{"description":"Refuse to issue an action if the redacted command or exact target changed since the displayed passive preview.","maxLength":64,"minLength":64,"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"issueAction":{"description":"Issue a one-time execution token. Defaults to true; passive previews should set false.","type":"boolean"},"revealCommand":{"description":"Return the expanded command for an explicit insert, run, or copy action. Normal previews remain redacted.","type":"boolean"},"snippetId":{"type":"string"},"submit":{"description":"Bind the preview to sending Enter after the command. Defaults to true.","type":"boolean"},"targets":{"items":{"$ref":"#/components/schemas/TerminalCommandTargetRequest"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs","targets"],"type":"object"},"TerminalCommandPreviewTarget":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"command":{"type":"string"},"sessionId":{"type":"string"},"targetId":{"type":"string"},"title":{"type":"string"}},"required":["targetId","sessionId","alias","title","command"],"type":"object"},"TerminalCommandResult":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"problem":{"type":"string"},"sessionId":{"type":"string"},"status":{"enum":["delivered","failed"],"type":"string"},"targetId":{"type":"string"},"title":{"type":"string"}},"required":["targetId","sessionId","alias","title","status"],"type":"object"},"TerminalCommandTargetRequest":{"additionalProperties":false,"properties":{"sessionId":{"maxLength":64,"minLength":1,"type":"string"},"targetId":{"maxLength":255,"minLength":1,"type":"string"}},"required":["targetId","sessionId"],"type":"object"},"TerminalConnectionProgress":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hop":{"minimum":1,"type":"integer"},"hops":{"minimum":1,"type":"integer"},"hostName":{"type":"string"},"phase":{"enum":["dialing","host_key","authenticating","authenticated","opening_session"],"type":"string"},"user":{"type":"string"}},"required":["phase","alias","hostName","user","hop","hops"],"type":"object","x-sshc-less-than-or-equal":[["hop","hops"]]},"TerminalControlCursor":{"additionalProperties":false,"properties":{"end":{"description":"Monotonic end of the raw transcript; greater than next means unread bytes remain.","format":"uint64","minimum":0,"type":"integer"},"next":{"format":"uint64","minimum":0,"type":"integer"},"requested":{"format":"uint64","minimum":0,"type":"integer"},"start":{"format":"uint64","minimum":0,"type":"integer"},"truncated":{"type":"boolean"}},"required":["requested","start","next","end","truncated"],"type":"object"},"TerminalControlResponse":{"additionalProperties":false,"properties":{"cursor":{"$ref":"#/components/schemas/TerminalControlCursor"},"generation":{"format":"uint64","minimum":1,"type":"integer"},"output":{"description":"Bounded transcript with terminal escape and control sequences removed.","type":"string"},"session":{"$ref":"#/components/schemas/TerminalSession"},"state":{"enum":["connecting","connected","reconnecting","exited"],"type":"string"}},"required":["session","generation","state","cursor","output"],"type":"object"},"TerminalDestination":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hostName":{"type":"string"},"port":{"type":"string"},"user":{"type":"string"}},"required":["alias","hostName","port","user"],"type":"object"},"TerminalDeterminateProgress":{"additionalProperties":false,"properties":{"percent":{"maximum":100,"minimum":0,"type":"integer"},"state":{"enum":["normal","error","paused"],"type":"string"}},"required":["state","percent"],"type":"object"},"TerminalDirectory":{"additionalProperties":false,"properties":{"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path"],"type":"object"},"TerminalExit":{"additionalProperties":false,"properties":{"at":{"type":"string"},"code":{"type":"integer"},"connectionLost":{"type":"boolean"},"signal":{"type":"string"}},"required":["code","signal","at"],"type":"object"},"TerminalFinishedCommand":{"additionalProperties":false,"properties":{"exitCode":{"type":"integer"},"seconds":{"minimum":0,"type":"integer"}},"required":["seconds"],"type":"object"},"TerminalForward":{"additionalProperties":false,"properties":{"id":{"maxLength":64,"minLength":1,"type":"string"},"kind":{"pattern":"^(local|remote|dynamic|agent)$","type":"string"},"listen":{"type":"string"},"problem":{"type":"string"},"temporary":{"type":"boolean"},"to":{"type":"string"}},"required":["id","kind","listen","to","problem","temporary"],"type":"object"},"TerminalHostKeyAnswer":{"additionalProperties":false,"properties":{"accept":{"type":"boolean"},"fingerprint":{"maxLength":128,"minLength":1,"type":"string"}},"required":["fingerprint","accept"],"type":"object"},"TerminalHostKeyQuestion":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"fingerprint":{"type":"string"},"hostName":{"type":"string"},"keyType":{"type":"string"}},"required":["alias","hostName","keyType","fingerprint"],"type":"object"},"TerminalIndeterminateProgress":{"additionalProperties":false,"properties":{"state":{"enum":["indeterminate"],"type":"string"}},"required":["state"],"type":"object"},"TerminalNotification":{"additionalProperties":false,"properties":{"body":{"type":"string"},"finishedCommand":{"$ref":"#/components/schemas/TerminalFinishedCommand"},"occurredAt":{"format":"date-time","type":"string"},"title":{"type":"string"}},"required":["title","body","occurredAt"],"type":"object"},"TerminalPasswordSaveAnswer":{"additionalProperties":false,"properties":{"save":{"type":"boolean"}},"required":["save"],"type":"object"},"TerminalPresentation":{"additionalProperties":false,"properties":{"displayTitle":{"type":"string"},"titlePinned":{"type":"boolean"},"titleSource":{"enum":["user","terminal","connection","fallback"],"type":"string"}},"required":["displayTitle","titleSource","titlePinned"],"type":"object"},"TerminalProgramProgress":{"oneOf":[{"$ref":"#/components/schemas/TerminalDeterminateProgress"},{"$ref":"#/components/schemas/TerminalIndeterminateProgress"}],"x-go-type":"json.RawMessage"},"TerminalProgramStatus":{"additionalProperties":false,"properties":{"app":{"maxLength":32,"type":"string"},"kind":{"enum":["permission","question","auth"],"type":"string"},"message":{"type":"string"},"progress":{"maximum":100,"minimum":0,"type":"integer"},"state":{"enum":["idle","working","blocked","done","error"],"type":"string"},"title":{"type":"string"},"version":{"minimum":1,"type":"integer"}},"required":["state","version"],"type":"object"},"TerminalReconnect":{"additionalProperties":false,"properties":{"attempt":{"maximum":5,"minimum":1,"type":"integer"},"limit":{"maximum":5,"minimum":1,"type":"integer"},"problem":{"type":"string"},"retryAt":{"format":"date-time","type":"string"},"waitingForNetwork":{"type":"boolean"}},"required":["attempt","limit","retryAt","problem"],"type":"object","x-sshc-less-than-or-equal":[["attempt","limit"]]},"TerminalRestoreOffer":{"additionalProperties":false,"properties":{"sessions":{"items":{"$ref":"#/components/schemas/TerminalRestoreSession"},"maxItems":256,"type":"array"}},"required":["sessions"],"type":"object"},"TerminalRestoreResult":{"additionalProperties":false,"properties":{"failed":{"minimum":0,"type":"integer"},"opened":{"minimum":0,"type":"integer"}},"required":["opened","failed"],"type":"object"},"TerminalRestoreSession":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"kind":{"enum":["ssh","shell"],"type":"string","x-enum-varnames":["TerminalRestoreSessionKindSsh","TerminalRestoreSessionKindShell"]},"pinnedTitle":{"type":"string"},"workingDirectory":{"type":"string"},"workspaceId":{"type":"string"}},"required":["kind","workspaceId"],"type":"object"},"TerminalResumeHint":{"additionalProperties":false,"properties":{"command":{"maxLength":8192,"type":"string"},"program":{"maxLength":64,"type":"string"}},"required":["program","command"],"type":"object"},"TerminalSession":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"awaitingPrompt":{"type":"boolean"},"exited":{"$ref":"#/components/schemas/TerminalExit"},"forwards":{"items":{"$ref":"#/components/schemas/TerminalForward"},"type":"array"},"hostKeyQuestion":{"$ref":"#/components/schemas/TerminalHostKeyQuestion"},"id":{"type":"string"},"kind":{"enum":["ssh","shell"],"type":"string"},"lastNotification":{"$ref":"#/components/schemas/TerminalNotification"},"notificationVersion":{"minimum":0,"type":"integer"},"passwordSaveQuestion":{"type":"boolean"},"presentation":{"$ref":"#/components/schemas/TerminalPresentation"},"problem":{"type":"string"},"programProgress":{"$ref":"#/components/schemas/TerminalProgramProgress"},"programStatus":{"$ref":"#/components/schemas/TerminalProgramStatus"},"progress":{"$ref":"#/components/schemas/TerminalConnectionProgress"},"reconnect":{"$ref":"#/components/schemas/TerminalReconnect"},"resume":{"$ref":"#/components/schemas/TerminalResumeHint"},"shellProfileId":{"type":"string"},"startedAt":{"type":"string"},"state":{"enum":["connecting","connected","reconnecting","exited"],"type":"string"},"title":{"type":"string"},"workingDirectory":{"type":"string"},"workspaceId":{"type":"string"}},"required":["id","kind","title","workspaceId","startedAt","state","problem"],"type":"object"},"TerminalSessionList":{"additionalProperties":false,"properties":{"maxSessions":{"maximum":200,"minimum":0,"type":"integer"},"restoreOffer":{"maxLength":64,"minLength":1,"type":"string"},"sessions":{"items":{"$ref":"#/components/schemas/TerminalSession"},"type":"array"}},"required":["sessions","maxSessions"],"type":"object"},"TerminalStreamTicket":{"additionalProperties":false,"properties":{"streamTicket":{"type":"string"}},"required":["streamTicket"],"type":"object"},"TerminalWorkspace":{"additionalProperties":false,"properties":{"builtIn":{"type":"boolean"},"hidden":{"type":"boolean"},"id":{"type":"string"},"name":{"maxLength":64,"type":"string"}},"required":["id","name","hidden","builtIn"],"type":"object"},"TerminalWorkspaceList":{"additionalProperties":false,"properties":{"maxWorkspaces":{"minimum":1,"type":"integer"},"workspaces":{"items":{"$ref":"#/components/schemas/TerminalWorkspace"},"type":"array"}},"required":["workspaces","maxWorkspaces"],"type":"object"},"TrashEntrySummary":{"additionalProperties":false,"properties":{"ageDays":{"type":"integer"},"blockers":{"items":{"type":"string"},"type":"array"},"files":{"items":{"$ref":"#/components/schemas/TrashFileSummary"},"type":"array"},"id":{"type":"string"},"restorable":{"type":"boolean"},"stale":{"type":"boolean"}},"required":["id","ageDays","stale","files","restorable","blockers"],"type":"object"},"TrashFileSummary":{"additionalProperties":false,"properties":{"fingerprint":{"type":"string"},"kind":{"type":"string"},"originalRelativePath":{"type":"string"},"permission":{"type":"string"}},"required":["originalRelativePath","kind","fingerprint","permission"],"type":"object"},"TrashKeyResponse":{"additionalProperties":false,"properties":{"entryId":{"type":"string"},"files":{"items":{"$ref":"#/components/schemas/TrashFileSummary"},"type":"array"},"skipped":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["entryId","files","skipped","transactionId"],"type":"object"},"TrashListResponse":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/TrashEntrySummary"},"type":"array"},"retentionDays":{"type":"integer"}},"required":["entries","retentionDays"],"type":"object"},"UnreadableFile":{"additionalProperties":false,"properties":{"reason":{"type":"string"},"relativePath":{"type":"string"}},"required":["relativePath","reason"],"type":"object"},"UnresolvedReference":{"additionalProperties":false,"properties":{"configPath":{"type":"string"},"directive":{"type":"string"},"line":{"type":"integer"},"reason":{"type":"string"},"value":{"type":"string"}},"required":["directive","value","configPath","line","reason"],"type":"object"},"UpdateConnectionKeyPassphrase":{"discriminator":{"mapping":{"set_dedicated":"#/components/schemas/ConnectionKeyPassphraseSetDedicated","unchanged":"#/components/schemas/ConnectionKeyPassphraseUnchanged"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/ConnectionKeyPassphraseUnchanged"},{"$ref":"#/components/schemas/ConnectionKeyPassphraseSetDedicated"}],"x-go-type":"json.RawMessage"},"UpdateConnectionPassword":{"discriminator":{"mapping":{"confirm_route":"#/components/schemas/UpdatePasswordConfirmRoute","dedicated_password":"#/components/schemas/CreateDedicatedPasswordAuthentication","new_shared_password":"#/components/schemas/CreateNewSharedPasswordAuthentication","remove":"#/components/schemas/UpdatePasswordRemove","saved_password":"#/components/schemas/CreateSavedPasswordAuthentication","unchanged":"#/components/schemas/UpdatePasswordUnchanged"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/UpdatePasswordUnchanged"},{"$ref":"#/components/schemas/CreateDedicatedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateSavedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateNewSharedPasswordAuthentication"},{"$ref":"#/components/schemas/UpdatePasswordConfirmRoute"},{"$ref":"#/components/schemas/UpdatePasswordRemove"}],"x-go-type":"json.RawMessage"},"UpdateConnectionRequest":{"additionalProperties":false,"properties":{"base":{"maxLength":1048576,"type":"string"},"hostName":{"$ref":"#/components/schemas/ConnectionStringChange"},"identity":{"$ref":"#/components/schemas/HostIdentity"},"identityFile":{"$ref":"#/components/schemas/ConnectionIdentityFileChange"},"keyPassphrase":{"$ref":"#/components/schemas/UpdateConnectionKeyPassphrase"},"password":{"$ref":"#/components/schemas/UpdateConnectionPassword"},"port":{"$ref":"#/components/schemas/ConnectionPortChange"},"totp":{"$ref":"#/components/schemas/UpdateConnectionTOTP"},"user":{"$ref":"#/components/schemas/ConnectionStringChange"}},"required":["identity","base","password","keyPassphrase","totp"],"type":"object"},"UpdateConnectionTOTP":{"discriminator":{"mapping":{"confirm_route":"#/components/schemas/UpdateTOTPConfirmRoute","remove":"#/components/schemas/UpdateTOTPRemove","saved_totp":"#/components/schemas/UpdateTOTPSaved","unchanged":"#/components/schemas/UpdateTOTPUnchanged"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/UpdateTOTPUnchanged"},{"$ref":"#/components/schemas/UpdateTOTPSaved"},{"$ref":"#/components/schemas/UpdateTOTPConfirmRoute"},{"$ref":"#/components/schemas/UpdateTOTPRemove"}],"x-go-type":"json.RawMessage"},"UpdateCredentialRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":128,"minLength":1,"type":"string"},"secret":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["name","secret"],"type":"object"},"UpdateJob":{"additionalProperties":false,"properties":{"id":{"type":"string"},"installedVersion":{"description":"Stable version reported by the installed executable.","type":"string"},"problem":{"type":"string"},"state":{"enum":["accepted","installing","restarting","succeeded","failed","restart_required"],"type":"string"},"target":{"type":"string"}},"required":["id","target","state","problem"],"type":"object"},"UpdateLog":{"additionalProperties":false,"properties":{"cursor":{"$ref":"#/components/schemas/TerminalControlCursor"},"jobId":{"type":"string"},"output":{"maxLength":65536,"type":"string"},"reachedCapacity":{"type":"boolean"}},"required":["jobId","cursor","reachedCapacity","output"],"type":"object"},"UpdatePasswordConfirmRoute":{"additionalProperties":false,"properties":{"kind":{"enum":["confirm_route"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdatePasswordRemove":{"additionalProperties":false,"properties":{"kind":{"enum":["remove"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdatePasswordUnchanged":{"additionalProperties":false,"properties":{"kind":{"enum":["unchanged"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdatePreview":{"additionalProperties":false,"properties":{"actionToken":{"type":"string"},"current":{"type":"string"},"manager":{"enum":["homebrew","install.sh","install.ps1"],"type":"string"},"target":{"type":"string"}},"required":["current","target","manager","actionToken"],"type":"object"},"UpdateRequest":{"additionalProperties":false,"properties":{"target":{"maxLength":128,"minLength":1,"type":"string"}},"required":["target"],"type":"object"},"UpdateStatus":{"additionalProperties":false,"properties":{"available":{"type":"boolean"},"canUpdate":{"type":"boolean"},"checkError":{"description":"Failure of the last check; previous successful release information is retained.","type":"string"},"checkedAt":{"description":"Completion time of the last check","format":"date-time","successful or failed.":null,"type":"string"},"current":{"type":"string"},"job":{"$ref":"#/components/schemas/UpdateJob"},"latest":{"type":"string"},"manager":{"enum":["homebrew","install.sh","install.ps1"],"type":"string"},"pageUrl":{"type":"string"},"publishedAt":{"format":"date-time","type":"string"},"reason":{"description":"Stable reason code explaining why automatic updates are unavailable","type":"string"},"releases":{"items":{"$ref":"#/components/schemas/ReleaseNotes"},"maxItems":100,"type":"array"}},"required":["current","available"],"type":"object"},"UpdateTOTPConfirmRoute":{"additionalProperties":false,"properties":{"kind":{"enum":["confirm_route"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdateTOTPRemove":{"additionalProperties":false,"properties":{"kind":{"enum":["remove"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdateTOTPSaved":{"additionalProperties":false,"properties":{"credential":{"maxLength":128,"minLength":1,"type":"string"},"kind":{"enum":["saved_totp"],"type":"string","x-go-type":"string"}},"required":["kind","credential"],"type":"object"},"UpdateTOTPUnchanged":{"additionalProperties":false,"properties":{"kind":{"enum":["unchanged"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdateTerminalWorkspaceRequest":{"additionalProperties":false,"properties":{"hidden":{"type":"boolean"},"name":{"maxLength":64,"type":"string"}},"type":"object"},"VPNBindingRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"profile":{"maxLength":48,"type":"string"}},"required":["alias","profile"],"type":"object"},"VPNLogs":{"additionalProperties":false,"properties":{"lines":{"maxLength":65536,"type":"string"}},"required":["lines"],"type":"object"},"VPNOverview":{"additionalProperties":false,"properties":{"available":{"type":"boolean"},"checking":{"type":"boolean"},"detail":{"type":"string"},"profiles":{"items":{"$ref":"#/components/schemas/VPNProfileStatus"},"maxItems":64,"type":"array"},"unavailable":{"enum":["vpn_docker_missing","vpn_docker_not_running"],"type":"string"}},"required":["available","profiles","checking"],"type":"object"},"VPNProfile":{"additionalProperties":false,"properties":{"backend":{"enum":["wireguard","l2tp_ipsec","openconnect","openvpn","ikev2"],"type":"string"},"dns":{"items":{"maxLength":15,"minLength":7,"type":"string"},"maxItems":3,"type":"array"},"id":{"pattern":"^[0-9a-f]{32}$","type":"string"},"ikev2":{"$ref":"#/components/schemas/IKEv2Profile"},"l2tp":{"$ref":"#/components/schemas/L2TPProfile"},"name":{"maxLength":48,"minLength":1,"type":"string"},"openconnect":{"$ref":"#/components/schemas/OpenConnectProfile"},"openvpn":{"$ref":"#/components/schemas/OpenVPNProfile"},"wireguard":{"$ref":"#/components/schemas/WireGuardProfile"}},"required":["name","backend"],"type":"object"},"VPNProfileRequest":{"additionalProperties":false,"properties":{"profile":{"$ref":"#/components/schemas/VPNProfile"},"secrets":{"$ref":"#/components/schemas/VPNSecrets"}},"required":["profile"],"type":"object"},"VPNProfileStatus":{"additionalProperties":false,"properties":{"connections":{"items":{"type":"string"},"type":"array"},"openConnections":{"minimum":0,"type":"integer"},"phase":{"enum":["image","container","tunnel","approval"],"type":"string"},"profile":{"$ref":"#/components/schemas/VPNProfile"},"relaySocket":{"type":"string"},"running":{"type":"boolean"},"tunnel":{"$ref":"#/components/schemas/VPNTunnel"}},"required":["profile","running","relaySocket","connections","openConnections"],"type":"object"},"VPNRenameRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":48,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"VPNSecrets":{"additionalProperties":false,"properties":{"ikev2Password":{"maxLength":256,"minLength":1,"type":"string"},"ikev2Psk":{"maxLength":256,"minLength":1,"type":"string"},"ipsecPsk":{"maxLength":256,"minLength":1,"type":"string"},"l2tpPassword":{"maxLength":256,"minLength":1,"type":"string"},"openconnectPassword":{"maxLength":256,"minLength":1,"type":"string"},"openconnectTotpSecret":{"maxLength":512,"minLength":1,"type":"string"},"openvpnConfig":{"maxLength":65536,"minLength":1,"type":"string"},"openvpnPassword":{"maxLength":256,"minLength":1,"type":"string"},"wireguardConfig":{"maxLength":16384,"minLength":1,"type":"string"}},"type":"object"},"VPNTunnel":{"additionalProperties":false,"properties":{"address":{"type":"string"},"backend":{"type":"string"},"interface":{"type":"string"},"since":{"type":"string"}},"type":"object"},"ValueSource":{"additionalProperties":false,"properties":{"condition":{"type":"string"},"keyword":{"type":"string"},"kind":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"},"value":{"type":"string"},"winner":{"type":"boolean"}},"required":["keyword","value","path","line","condition","kind","winner"],"type":"object"},"VaultAutoLockSettings":{"additionalProperties":false,"properties":{"mode":{"description":"idleは無操作時間後、restartは手動ロックまたはengine終了時だけロックする。","enum":["idle","restart"],"type":"string"},"unit":{"enum":["minutes","hours"],"type":"string"},"value":{"maximum":999,"minimum":1,"type":"integer"}},"required":["mode"],"type":"object"},"WireGuardProfile":{"additionalProperties":false,"properties":{"servers":{"items":{"maxLength":320,"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"}},"required":["servers"],"type":"object"}} as const satisfies Readonly>; -const endpoints = [{"method":"GET","path":"/api/v1/about","responses":{"200":{"$ref":"#/components/schemas/About"}}},{"method":"POST","path":"/api/v1/actions","requestRequired":true,"request":{"$ref":"#/components/schemas/IssueActionRequest"},"responses":{"201":{"$ref":"#/components/schemas/IssueActionResponse"}}},{"method":"GET","path":"/api/v1/config/file","responses":{"200":{"$ref":"#/components/schemas/FileContents"}}},{"method":"POST","path":"/api/v1/config/groups/delete","requestRequired":true,"request":{"$ref":"#/components/schemas/GroupDeleteRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"POST","path":"/api/v1/config/groups/rename","requestRequired":true,"request":{"$ref":"#/components/schemas/GroupRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"GET","path":"/api/v1/config/host","responses":{"200":{"$ref":"#/components/schemas/HostDetail"}}},{"method":"GET","path":"/api/v1/config/overview","responses":{"200":{"$ref":"#/components/schemas/Overview"}}},{"method":"POST","path":"/api/v1/config/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/EditRequest"},"responses":{"200":{"$ref":"#/components/schemas/SavePreview"}}},{"method":"POST","path":"/api/v1/config/save","requestRequired":true,"request":{"$ref":"#/components/schemas/EditRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"PATCH","path":"/api/v1/connections","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateConnectionRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"POST","path":"/api/v1/connections","requestRequired":true,"request":{"$ref":"#/components/schemas/CreateConnectionRequest"},"responses":{"201":{"$ref":"#/components/schemas/CreateConnectionResponse"}}},{"method":"GET","path":"/api/v1/connections/recent","responses":{"200":{"$ref":"#/components/schemas/RecentConnectionList"}}},{"method":"GET","path":"/api/v1/credentials","responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"POST","path":"/api/v1/credentials/totp/{name}/codes","responses":{"200":{"$ref":"#/components/schemas/TOTPCodeSet"}}},{"method":"PUT","path":"/api/v1/credentials/{kind}/assign","requestRequired":true,"request":{"$ref":"#/components/schemas/AssignCredentialRequest"},"responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"DELETE","path":"/api/v1/credentials/{kind}/assign/{subject}","responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"DELETE","path":"/api/v1/credentials/{kind}/{name}","responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"PATCH","path":"/api/v1/credentials/{kind}/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateCredentialRequest"},"responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"PUT","path":"/api/v1/credentials/{kind}/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/StoreCredentialRequest"},"responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"POST","path":"/api/v1/credentials/{kind}/{name}/reveal","responses":{"200":{"$ref":"#/components/schemas/RevealCredentialResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/authentication","requestRequired":true,"request":{"$ref":"#/components/schemas/AuthenticationRequest"},"responses":{"200":{"$ref":"#/components/schemas/AuthenticationResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/config","responses":{"200":{"$ref":"#/components/schemas/ConfigCheckResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/effective","requestRequired":true,"request":{"$ref":"#/components/schemas/AliasRequest"},"responses":{"200":{"$ref":"#/components/schemas/EffectiveResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/reachability","requestRequired":true,"request":{"$ref":"#/components/schemas/AliasRequest"},"responses":{"200":{"$ref":"#/components/schemas/ReachabilityResponse"}}},{"method":"GET","path":"/api/v1/engines","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineList"}}},{"method":"POST","path":"/api/v1/engines","requestRequired":true,"request":{"$ref":"#/components/schemas/AddRemoteEngineRequest"},"responses":{"201":{"$ref":"#/components/schemas/RemoteEngine"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/control","requestRequired":true,"request":{"$ref":"#/components/schemas/RemoteEngineActionRequest"},"responses":{"202":{"$ref":"#/components/schemas/RemoteEngineActionStarted"}}},{"method":"GET","path":"/api/v1/engines/{alias}/control/log","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineActionLog"}}},{"method":"POST","path":"/api/v1/engines/{alias}/open","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineEntrance"}}},{"method":"GET","path":"/api/v1/engines/{alias}/status","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineStatus"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/commands","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandDispatchRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandDispatchResponse"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/commands/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandPreviewRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandPreview"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/connection-history","responses":{"200":{"$ref":"#/components/schemas/ConnectionHistory"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/destinations/{connectionAlias}","responses":{"200":{"$ref":"#/components/schemas/TerminalDestination"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/machine","responses":{"200":{"$ref":"#/components/schemas/MachineStatus"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/restore","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreOffer"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreResult"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/sessions","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions","requestRequired":true,"request":{"$ref":"#/components/schemas/OpenTerminalSessionRequest"},"responses":{"201":{"$ref":"#/components/schemas/OpenTerminalSessionResponse"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/sessions/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/directory","responses":{"200":{"$ref":"#/components/schemas/TerminalDirectory"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/forwards","requestRequired":true,"request":{"$ref":"#/components/schemas/StartTerminalForwardRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/forwards/{forwardId}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/host-key","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalHostKeyAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/password-save","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalPasswordSaveAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/reconnect","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/reconnect/stop","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/stream","responses":{"201":{"$ref":"#/components/schemas/TerminalStreamTicket"}}},{"method":"PUT","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/title","requestRequired":true,"request":{"$ref":"#/components/schemas/SetTerminalSessionTitleRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"PUT","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/workspace","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalSessionRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/shell-profiles","responses":{"200":{"$ref":"#/components/schemas/LocalShellProfileList"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/workspaces","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/workspaces","requestRequired":true,"request":{"$ref":"#/components/schemas/CreateTerminalWorkspaceRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/workspaces/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"PATCH","path":"/api/v1/engines/{alias}/terminal/workspaces/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"PUT","path":"/api/v1/engines/{alias}/terminal/workspaces/{id}/position","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/vault/unlock","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/health","responses":{"200":{"$ref":"#/components/schemas/HealthResponse"}}},{"method":"GET","path":"/api/v1/history","responses":{"200":{"$ref":"#/components/schemas/HistoryList"}}},{"method":"GET","path":"/api/v1/history/pending/{id}","responses":{"200":{"$ref":"#/components/schemas/PendingChanges"}}},{"method":"POST","path":"/api/v1/history/recover","requestRequired":true,"request":{"$ref":"#/components/schemas/RecoverRequest"},"responses":{"200":{"$ref":"#/components/schemas/RecoverResponse"}}},{"method":"POST","path":"/api/v1/history/restore","requestRequired":true,"request":{"$ref":"#/components/schemas/RestoreRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"GET","path":"/api/v1/keys","responses":{"200":{"$ref":"#/components/schemas/KeyInventoryResponse"}}},{"method":"POST","path":"/api/v1/keys","requestRequired":true,"request":{"$ref":"#/components/schemas/GenerateKeyRequest"},"responses":{"201":{"$ref":"#/components/schemas/GenerateKeyResponse"}}},{"method":"GET","path":"/api/v1/keys/algorithms","responses":{"200":{"$ref":"#/components/schemas/KeyAlgorithmsResponse"}}},{"method":"POST","path":"/api/v1/keys/hardware-command","requestRequired":true,"request":{"$ref":"#/components/schemas/HardwareCommandRequest"},"responses":{"200":{"$ref":"#/components/schemas/HardwareCommandResponse"}}},{"method":"DELETE","path":"/api/v1/keys/{keyId}/agent","responses":{"200":{"$ref":"#/components/schemas/AgentIdentitiesResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/agent","requestRequired":true,"request":{"$ref":"#/components/schemas/RegisterKeyRequest"},"responses":{"200":{"$ref":"#/components/schemas/RegisterKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/location","requestRequired":true,"request":{"$ref":"#/components/schemas/RelocateKeyRequest"},"responses":{"200":{"$ref":"#/components/schemas/RelocateKeyResponse"},"409":{"$ref":"#/components/schemas/RelocateKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/passphrase","requestRequired":true,"request":{"$ref":"#/components/schemas/ChangePassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/ChangePassphraseResponse"}}},{"method":"GET","path":"/api/v1/keys/{keyId}/public","responses":{"200":{"$ref":"#/components/schemas/PublicKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/reveal","responses":{"200":{"$ref":"#/components/schemas/RevealPrivateKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/trash","responses":{"200":{"$ref":"#/components/schemas/TrashKeyResponse"}}},{"method":"GET","path":"/api/v1/known-hosts","responses":{"200":{"$ref":"#/components/schemas/KnownHostsResponse"}}},{"method":"POST","path":"/api/v1/known-hosts/add","requestRequired":true,"request":{"$ref":"#/components/schemas/KnownHostsAddRequest"},"responses":{"200":{"$ref":"#/components/schemas/KnownHostsChangeResponse"}}},{"method":"POST","path":"/api/v1/known-hosts/delete","requestRequired":true,"request":{"$ref":"#/components/schemas/KnownHostsDeleteRequest"},"responses":{"200":{"$ref":"#/components/schemas/KnownHostsChangeResponse"}}},{"method":"POST","path":"/api/v1/known-hosts/scan","requestRequired":true,"request":{"$ref":"#/components/schemas/KnownHostsScanRequest"},"responses":{"200":{"$ref":"#/components/schemas/KnownHostsScanResponse"}}},{"method":"GET","path":"/api/v1/metadata","responses":{"200":{"$ref":"#/components/schemas/Metadata"}}},{"method":"PUT","path":"/api/v1/metadata/keybar","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"items":{"$ref":"#/components/schemas/KeyBarPreset"},"maxItems":64,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/KeyBarPreset"},"maxItems":64,"type":"array"}},"required":["base","presets"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/metadata/shortcuts","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"items":{"$ref":"#/components/schemas/ShortcutPreset"},"maxItems":64,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/ShortcutPreset"},"maxItems":64,"type":"array"}},"required":["base","presets"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"GET","path":"/api/v1/passwords","responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/change","requestRequired":true,"request":{"$ref":"#/components/schemas/ChangeMasterPasswordRequest"},"responses":{"200":{"$ref":"#/components/schemas/ChangeMasterPasswordResult"}}},{"method":"POST","path":"/api/v1/passwords/initialise","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/lock","responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/recover-compatible-backup","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/reset-unsupported","requestRequired":true,"request":{"$ref":"#/components/schemas/ResetUnsupportedVaultRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/unlock","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"GET","path":"/api/v1/passwords/{alias}/eligibility","responses":{"200":{"$ref":"#/components/schemas/PasswordEligibility"}}},{"method":"POST","path":"/api/v1/remote-keys/plan","requestRequired":true,"request":{"$ref":"#/components/schemas/RemoteKeyPlanRequest"},"responses":{"200":{"$ref":"#/components/schemas/RemoteKeyPlan"}}},{"method":"POST","path":"/api/v1/remote-keys/register","requestRequired":true,"request":{"$ref":"#/components/schemas/RemoteKeyRegisterRequest"},"responses":{"200":{"$ref":"#/components/schemas/RemoteKeyRegisterResponse"}}},{"method":"POST","path":"/api/v1/session/bootstrap","responses":{"200":{"$ref":"#/components/schemas/BootstrapResponse"}}},{"method":"POST","path":"/api/v1/session/recover","responses":{"200":{"$ref":"#/components/schemas/BootstrapResponse"}}},{"method":"POST","path":"/api/v1/session/renew","responses":{"200":{"$ref":"#/components/schemas/BootstrapResponse"}}},{"method":"POST","path":"/api/v1/session/sign-out","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/settings","responses":{"200":{"$ref":"#/components/schemas/SettingsDocument"}}},{"method":"PUT","path":"/api/v1/settings/global","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"$ref":"#/components/schemas/GlobalSettings"},"settings":{"$ref":"#/components/schemas/GlobalSettings"}},"required":["base","settings"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"DELETE","path":"/api/v1/settings/machines/{id}","responses":{},"noContent":["204"]},{"method":"PUT","path":"/api/v1/settings/machines/{id}","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"$ref":"#/components/schemas/MachineSettings"},"settings":{"$ref":"#/components/schemas/MachineSettings"}},"required":["base","settings"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/settings/presets","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"}},"required":["base","presets"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/settings/presets/{id}","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"$ref":"#/components/schemas/SettingsPreset"},"preset":{"$ref":"#/components/schemas/SettingsPreset"}},"required":["base","preset"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/settings/state","requestRequired":true,"request":{"additionalProperties":false,"minProperties":1,"properties":{"interface":{"$ref":"#/components/schemas/InterfaceState"},"transfersStopped":{"type":"boolean"}},"type":"object"},"responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/sftp/compare","responses":{"200":{"$ref":"#/components/schemas/SFTPDirectoryComparison"}}},{"method":"POST","path":"/api/v1/sftp/local/delete","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalDeleteRequest"},"responses":{},"noContent":["204"]},{"method":"POST","path":"/api/v1/sftp/local/delete-plan","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalDeleteSelection"},"responses":{"200":{"$ref":"#/components/schemas/SFTPLocalDeletePlan"}}},{"method":"POST","path":"/api/v1/sftp/local/directories","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalMkdirRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/local/download","responses":{},"noContent":["416"]},{"method":"GET","path":"/api/v1/sftp/local/entries","responses":{"200":{"$ref":"#/components/schemas/SFTPLocalListing"}}},{"method":"GET","path":"/api/v1/sftp/local/preview","responses":{}},{"method":"POST","path":"/api/v1/sftp/local/rename","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/local/text","responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"PUT","path":"/api/v1/sftp/local/text","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPSaveTextRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"GET","path":"/api/v1/sftp/local/thumbnail","responses":{}},{"method":"POST","path":"/api/v1/sftp/media-tickets","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPMediaTicketRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPMediaTicket"}}},{"method":"DELETE","path":"/api/v1/sftp/media-tickets/{ticket}","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/sftp/media/{ticket}","responses":{},"noContent":["416"]},{"method":"GET","path":"/api/v1/sftp/transfers","responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJobList"}}},{"method":"POST","path":"/api/v1/sftp/transfers","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCreateTransferJobRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPTransferJob"}}},{"method":"DELETE","path":"/api/v1/sftp/transfers/finished","responses":{},"noContent":["204"]},{"method":"PUT","path":"/api/v1/sftp/transfers/settings","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPTransferSettingsRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJobList"}}},{"method":"DELETE","path":"/api/v1/sftp/transfers/{id}","responses":{},"noContent":["204"]},{"method":"POST","path":"/api/v1/sftp/transfers/{id}/actions","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPTransferJobActionRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJob"}}},{"method":"POST","path":"/api/v1/sftp/transfers/{id}/download-checkpoint","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPDownloadCheckpointRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJob"}}},{"method":"POST","path":"/api/v1/sftp/transfers/{id}/queue-position","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPTransferQueueMoveRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJobList"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/archive","responses":{}},{"method":"GET","path":"/api/v1/sftp/{alias}/connection-attempts/{attempt}","responses":{"200":{"$ref":"#/components/schemas/SFTPConnectionAttempt"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/connection-attempts/{attempt}/host-key","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalHostKeyAnswer"},"responses":{"200":{"$ref":"#/components/schemas/SFTPConnectionAttempt"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/download","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/sftp/{alias}/entries","responses":{"200":{"$ref":"#/components/schemas/SFTPListing"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/entries","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCreateEntryRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/entry","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/mode","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChmodRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/mode-plan","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChmodSelection"},"responses":{"200":{"$ref":"#/components/schemas/SFTPChmodPlan"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/modes","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChmodSelectionRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPChmodResult"},"207":{"$ref":"#/components/schemas/SFTPChmodResult"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/ownership","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPOwnershipRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/preview","responses":{}},{"method":"GET","path":"/api/v1/sftp/{alias}/search","responses":{"200":{"$ref":"#/components/schemas/SFTPSearchResult"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/space","responses":{"200":{"$ref":"#/components/schemas/SFTPFilesystemSpace"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/stats","responses":{"200":{"$ref":"#/components/schemas/SFTPDirectoryStats"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/symlink","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChangeSymlinkRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/symlink","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCreateSymlinkRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/text","responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"PUT","path":"/api/v1/sftp/{alias}/text","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPSaveTextRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/thumbnail","responses":{}},{"method":"DELETE","path":"/api/v1/sftp/{alias}/uploads/{id}","responses":{"200":{"$ref":"#/components/schemas/ChangedResponse"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/uploads/{id}","requestRequired":true,"responses":{"200":{"$ref":"#/components/schemas/SFTPResumableUpload"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/uploads/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPStartUploadRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPResumableUpload"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/uploads/{id}/complete","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCompleteUploadRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPTransfer"}}},{"method":"GET","path":"/api/v1/snippets","responses":{"200":{"$ref":"#/components/schemas/SnippetLibrary"}}},{"method":"POST","path":"/api/v1/snippets","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetDraft"},"responses":{"201":{"$ref":"#/components/schemas/Snippet"}}},{"method":"POST","path":"/api/v1/snippets/jobs","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetExecuteRequest"},"responses":{"202":{"$ref":"#/components/schemas/SnippetJob"}}},{"method":"DELETE","path":"/api/v1/snippets/jobs/{id}","responses":{"202":{"$ref":"#/components/schemas/SnippetJob"}}},{"method":"GET","path":"/api/v1/snippets/jobs/{id}","responses":{"200":{"$ref":"#/components/schemas/SnippetJob"}}},{"method":"POST","path":"/api/v1/snippets/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetPreviewRequest"},"responses":{"200":{"$ref":"#/components/schemas/SnippetPreview"}}},{"method":"PUT","path":"/api/v1/snippets/startup/{alias}","requestRequired":true,"request":{"$ref":"#/components/schemas/StartupSnippetRequest"},"responses":{"200":{"$ref":"#/components/schemas/SnippetLibrary"}}},{"method":"DELETE","path":"/api/v1/snippets/{id}","responses":{"200":{"$ref":"#/components/schemas/ChangedResponse"}}},{"method":"PUT","path":"/api/v1/snippets/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetDraft"},"responses":{"200":{"$ref":"#/components/schemas/Snippet"}}},{"method":"GET","path":"/api/v1/sync","responses":{"200":{"$ref":"#/components/schemas/SyncStatus"}}},{"method":"PUT","path":"/api/v1/sync/auto","requestRequired":true,"request":{"$ref":"#/components/schemas/AutoSyncRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncStatus"}}},{"method":"GET","path":"/api/v1/sync/bucket","responses":{"200":{"$ref":"#/components/schemas/SyncBucketStatus"}}},{"method":"GET","path":"/api/v1/sync/exclusions","responses":{"200":{"$ref":"#/components/schemas/SyncExclusions"}}},{"method":"PUT","path":"/api/v1/sync/exclusions","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncExclusionsRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncExclusions"}}},{"method":"POST","path":"/api/v1/sync/force-push","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncPushRequest"},"responses":{"200":{"$ref":"#/components/schemas/PushResponse"}}},{"method":"GET","path":"/api/v1/sync/history","responses":{"200":{"$ref":"#/components/schemas/SyncHistory"}}},{"method":"POST","path":"/api/v1/sync/history/diff","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncHistoryDiffRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncHistoryDiff"}}},{"method":"PUT","path":"/api/v1/sync/key","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncKeyRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncKeyResponse"}}},{"method":"POST","path":"/api/v1/sync/now","responses":{"200":{"$ref":"#/components/schemas/SyncStatus"}}},{"method":"POST","path":"/api/v1/sync/pull","requestRequired":true,"request":{"$ref":"#/components/schemas/PullRequest"},"responses":{"200":{"$ref":"#/components/schemas/PullResponse"}}},{"method":"GET","path":"/api/v1/sync/push","responses":{"200":{"$ref":"#/components/schemas/SyncPushDraft"}}},{"method":"POST","path":"/api/v1/sync/push","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncPushRequest"},"responses":{"200":{"$ref":"#/components/schemas/PushResponse"}}},{"method":"PUT","path":"/api/v1/sync/setup","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncSetupRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncSetupResponse"}}},{"method":"POST","path":"/api/v1/sync/setup/check","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncSetupCheckRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncSetupCheckResponse"}}},{"method":"GET","path":"/api/v1/terminal/backgrounds","responses":{"200":{"$ref":"#/components/schemas/TerminalBackgroundList"}}},{"method":"POST","path":"/api/v1/terminal/backgrounds","requestRequired":true,"responses":{"201":{"$ref":"#/components/schemas/TerminalBackground"}}},{"method":"PUT","path":"/api/v1/terminal/backgrounds/capacity","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalBackgroundCapacityRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalBackgroundList"}}},{"method":"DELETE","path":"/api/v1/terminal/backgrounds/{name}","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/terminal/backgrounds/{name}","responses":{}},{"method":"PATCH","path":"/api/v1/terminal/backgrounds/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/RenameTerminalBackgroundRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalBackground"}}},{"method":"POST","path":"/api/v1/terminal/commands","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandDispatchRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandDispatchResponse"}}},{"method":"POST","path":"/api/v1/terminal/commands/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandPreviewRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandPreview"}}},{"method":"GET","path":"/api/v1/terminal/connection-history","responses":{"200":{"$ref":"#/components/schemas/ConnectionHistory"}}},{"method":"GET","path":"/api/v1/terminal/destinations/{connectionAlias}","responses":{"200":{"$ref":"#/components/schemas/TerminalDestination"}}},{"method":"GET","path":"/api/v1/terminal/machine","responses":{"200":{"$ref":"#/components/schemas/MachineStatus"}}},{"method":"DELETE","path":"/api/v1/terminal/restore","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreOffer"}}},{"method":"POST","path":"/api/v1/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreResult"}}},{"method":"GET","path":"/api/v1/terminal/sessions","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions","requestRequired":true,"request":{"$ref":"#/components/schemas/OpenTerminalSessionRequest"},"responses":{"201":{"$ref":"#/components/schemas/OpenTerminalSessionResponse"}}},{"method":"DELETE","path":"/api/v1/terminal/sessions/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/terminal/sessions/{id}/control","responses":{"200":{"$ref":"#/components/schemas/TerminalControlResponse"}}},{"method":"GET","path":"/api/v1/terminal/sessions/{id}/directory","responses":{"200":{"$ref":"#/components/schemas/TerminalDirectory"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/forwards","requestRequired":true,"request":{"$ref":"#/components/schemas/StartTerminalForwardRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"DELETE","path":"/api/v1/terminal/sessions/{id}/forwards/{forwardId}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/host-key","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalHostKeyAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/password-save","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalPasswordSaveAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/reconnect","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/reconnect/stop","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/stream","responses":{"201":{"$ref":"#/components/schemas/TerminalStreamTicket"}}},{"method":"PUT","path":"/api/v1/terminal/sessions/{id}/title","requestRequired":true,"request":{"$ref":"#/components/schemas/SetTerminalSessionTitleRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"PUT","path":"/api/v1/terminal/sessions/{id}/workspace","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalSessionRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/terminal/shell-profiles","responses":{"200":{"$ref":"#/components/schemas/LocalShellProfileList"}}},{"method":"GET","path":"/api/v1/terminal/workspaces","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"POST","path":"/api/v1/terminal/workspaces","requestRequired":true,"request":{"$ref":"#/components/schemas/CreateTerminalWorkspaceRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"DELETE","path":"/api/v1/terminal/workspaces/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"PATCH","path":"/api/v1/terminal/workspaces/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"PUT","path":"/api/v1/terminal/workspaces/{id}/position","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"GET","path":"/api/v1/trash","responses":{"200":{"$ref":"#/components/schemas/TrashListResponse"}}},{"method":"DELETE","path":"/api/v1/trash/{entryId}","responses":{"200":{"$ref":"#/components/schemas/PurgeTrashResponse"}}},{"method":"POST","path":"/api/v1/trash/{entryId}/restore","responses":{"200":{"$ref":"#/components/schemas/RestoreTrashResponse"},"409":{"$ref":"#/components/schemas/RestoreTrashResponse"}}},{"method":"GET","path":"/api/v1/update","responses":{"200":{"$ref":"#/components/schemas/UpdateStatus"}}},{"method":"POST","path":"/api/v1/update","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateRequest"},"responses":{"202":{"$ref":"#/components/schemas/UpdateJob"}}},{"method":"POST","path":"/api/v1/update/check","responses":{"200":{"$ref":"#/components/schemas/UpdateStatus"}}},{"method":"GET","path":"/api/v1/update/log","responses":{"200":{"$ref":"#/components/schemas/UpdateLog"}}},{"method":"POST","path":"/api/v1/update/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateRequest"},"responses":{"200":{"$ref":"#/components/schemas/UpdatePreview"}}},{"method":"GET","path":"/api/v1/vpn","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"PUT","path":"/api/v1/vpn/bindings","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNBindingRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"POST","path":"/api/v1/vpn/profiles","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNProfileRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"DELETE","path":"/api/v1/vpn/profiles/{name}","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"PUT","path":"/api/v1/vpn/profiles/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNProfileRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"GET","path":"/api/v1/vpn/profiles/{name}/logs","responses":{"200":{"$ref":"#/components/schemas/VPNLogs"}}},{"method":"POST","path":"/api/v1/vpn/profiles/{name}/rename","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"POST","path":"/api/v1/vpn/profiles/{name}/reveal","responses":{"200":{"$ref":"#/components/schemas/VPNSecrets"}}},{"method":"DELETE","path":"/api/v1/vpn/profiles/{name}/route","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"POST","path":"/api/v1/vpn/profiles/{name}/route","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}}] as const satisfies readonly Endpoint[]; +const componentSchemas = {"About":{"additionalProperties":false,"properties":{"arch":{"type":"string"},"commitTime":{"description":"VCS commit time recorded by Go; not the compilation time.","format":"date-time","type":"string"},"executable":{"type":"string"},"goVersion":{"type":"string"},"manager":{"enum":["homebrew","install.sh","install.ps1","unknown"],"type":"string"},"modified":{"type":"boolean"},"os":{"type":"string"},"revision":{"type":"string"},"version":{"type":"string"}},"required":["version","revision","goVersion","os","arch","manager","executable"],"type":"object"},"AddRemoteEngineRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"}},"required":["alias"],"type":"object"},"AgentIdentitiesResponse":{"additionalProperties":false,"properties":{"agentAvailable":{"type":"boolean"},"id":{"type":"string"},"identities":{"items":{"$ref":"#/components/schemas/AgentIdentity"},"type":"array"}},"required":["id","agentAvailable","identities"],"type":"object"},"AgentIdentity":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"bits":{"type":"integer"},"comment":{"type":"string"},"fingerprint":{"type":"string"}},"required":["bits","fingerprint","comment","algorithm"],"type":"object"},"AliasRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"}},"required":["alias"],"type":"object"},"AppearanceSettings":{"additionalProperties":false,"properties":{"accent":{"pattern":"^(lavender|blue|sky|pink|mono|#[0-9a-f]{6})$","type":"string"},"emptyTerminalArt":{"enum":["none","network","contours","globe","aquarium"],"type":"string"},"reduceMotion":{"type":"boolean"},"theme":{"pattern":"^[a-z0-9-]{1,32}$","type":"string"}},"type":"object"},"AssignCredentialRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":128,"minLength":1,"type":"string"},"subject":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["subject","name"],"type":"object"},"AuthenticationRequest":{"additionalProperties":false,"properties":{"acknowledgeExecutable":{"type":"boolean"},"alias":{"maxLength":64,"minLength":1,"type":"string"}},"required":["alias","acknowledgeExecutable"],"type":"object"},"AuthenticationResponse":{"additionalProperties":false,"properties":{"authenticated":{"type":"boolean"},"detail":{"type":"string"},"method":{"type":"string"},"outcome":{"type":"string"},"truncated":{"type":"boolean"}},"required":["outcome","authenticated","method","detail","truncated"],"type":"object"},"AutoSync":{"additionalProperties":false,"properties":{"at":{"type":"string"},"detail":{"type":"string"},"enabled":{"type":"boolean"},"phase":{"enum":["idle","running","blocked","failed"],"type":"string"}},"required":["enabled","phase"],"type":"object"},"AutoSyncRequest":{"additionalProperties":false,"properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"type":"object"},"BackgroundSettings":{"additionalProperties":false,"properties":{"capacityMiB":{"maximum":1024,"minimum":1,"type":"integer"}},"type":"object"},"BootstrapResponse":{"additionalProperties":false,"properties":{"browserToken":{"maxLength":43,"minLength":43,"pattern":"^[A-Za-z0-9_-]{43}$","type":"string"},"csrfToken":{"maxLength":43,"minLength":43,"pattern":"^[A-Za-z0-9_-]{43}$","type":"string"}},"required":["csrfToken"],"type":"object"},"ChangeMasterPasswordRequest":{"additionalProperties":false,"properties":{"current":{"maxLength":1024,"type":"string"},"next":{"maxLength":1024,"type":"string"}},"required":["current","next"],"type":"object"},"ChangeMasterPasswordResult":{"additionalProperties":false,"properties":{"vault":{"$ref":"#/components/schemas/PasswordVaultStatus"}},"required":["vault"],"type":"object"},"ChangePassphraseRequest":{"additionalProperties":false,"properties":{"currentPassphrase":{"maxLength":1024,"type":"string"},"newPassphrase":{"maxLength":1024,"type":"string"},"unencrypted":{"type":"boolean"}},"required":["currentPassphrase","newPassphrase","unencrypted"],"type":"object"},"ChangePassphraseResponse":{"additionalProperties":false,"properties":{"encrypted":{"type":"boolean"},"id":{"type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"relativePath":{"type":"string"},"transactionId":{"type":"string"}},"required":["id","relativePath","encrypted","notes","transactionId"],"type":"object"},"ChangedResponse":{"additionalProperties":false,"properties":{"changed":{"type":"boolean"}},"required":["changed"],"type":"object"},"ComplexityNote":{"additionalProperties":false,"properties":{"code":{"type":"string"},"condition":{"type":"string"},"detail":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"}},"required":["code","path","line","condition","detail"],"type":"object"},"ConfigCheckResponse":{"additionalProperties":false,"properties":{"diagnostics":{"items":{"$ref":"#/components/schemas/ConfigDiagnostic"},"type":"array"},"files":{"items":{"$ref":"#/components/schemas/ConfigFileSummary"},"type":"array"},"root":{"type":"string"}},"required":["root","files","diagnostics"],"type":"object"},"ConfigDiagnostic":{"additionalProperties":false,"properties":{"code":{"type":"string"},"detail":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"},"severity":{"type":"string"}},"required":["severity","code","path","line","detail"],"type":"object"},"ConfigFileSummary":{"additionalProperties":false,"properties":{"editable":{"type":"boolean"},"includes":{"type":"integer"},"loads":{"type":"integer"},"missing":{"type":"boolean"},"path":{"type":"string"}},"required":["path","editable","missing","loads","includes"],"type":"object"},"ConflictReport":{"additionalProperties":false,"properties":{"baseDigest":{"type":"string"},"diskDigest":{"type":"string"},"externalChange":{"items":{"$ref":"#/components/schemas/DiffLine"},"type":"array"},"localChange":{"items":{"$ref":"#/components/schemas/DiffLine"},"type":"array"},"path":{"type":"string"}},"required":["path","externalChange","localChange"],"type":"object"},"ConnectionHistory":{"additionalProperties":false,"properties":{"hours":{"items":{"$ref":"#/components/schemas/ConnectionHour"},"maxItems":8928,"type":"array"},"recent":{"items":{"$ref":"#/components/schemas/RecentConnection"},"maxItems":20,"type":"array"}},"required":["hours","recent"],"type":"object"},"ConnectionHour":{"additionalProperties":false,"properties":{"aliases":{"items":{"maxLength":64,"minLength":1,"type":"string"},"maxItems":32,"type":"array"},"connections":{"minimum":1,"type":"integer"},"hour":{"format":"date-time","type":"string"}},"required":["hour","connections","aliases"],"type":"object"},"ConnectionIdentityFileChange":{"discriminator":{"mapping":{"inherit":"#/components/schemas/ConnectionInherit","set":"#/components/schemas/ConnectionIdentityFileSet"},"propertyName":"action"},"oneOf":[{"$ref":"#/components/schemas/ConnectionIdentityFileSet"},{"$ref":"#/components/schemas/ConnectionInherit"}],"x-go-type":"json.RawMessage"},"ConnectionIdentityFileSet":{"additionalProperties":false,"properties":{"action":{"enum":["set"],"type":"string","x-go-type":"string"},"keyId":{"maxLength":32,"minLength":32,"type":"string"}},"required":["action","keyId"],"type":"object"},"ConnectionInherit":{"additionalProperties":false,"properties":{"action":{"enum":["inherit"],"type":"string","x-go-type":"string"}},"required":["action"],"type":"object"},"ConnectionKeyPassphraseSetDedicated":{"additionalProperties":false,"properties":{"keyId":{"maxLength":32,"minLength":32,"type":"string"},"kind":{"enum":["set_dedicated"],"type":"string","x-go-type":"string"},"passphrase":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["kind","keyId","passphrase"],"type":"object"},"ConnectionKeyPassphraseUnchanged":{"additionalProperties":false,"properties":{"kind":{"enum":["unchanged"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"ConnectionPortChange":{"discriminator":{"mapping":{"inherit":"#/components/schemas/ConnectionInherit","set":"#/components/schemas/ConnectionPortSet"},"propertyName":"action"},"oneOf":[{"$ref":"#/components/schemas/ConnectionPortSet"},{"$ref":"#/components/schemas/ConnectionInherit"}],"x-go-type":"json.RawMessage"},"ConnectionPortSet":{"additionalProperties":false,"properties":{"action":{"enum":["set"],"type":"string","x-go-type":"string"},"value":{"maximum":65535,"minimum":1,"type":"integer"}},"required":["action","value"],"type":"object"},"ConnectionStringChange":{"discriminator":{"mapping":{"inherit":"#/components/schemas/ConnectionInherit","set":"#/components/schemas/ConnectionStringSet"},"propertyName":"action"},"oneOf":[{"$ref":"#/components/schemas/ConnectionStringSet"},{"$ref":"#/components/schemas/ConnectionInherit"}],"x-go-type":"json.RawMessage"},"ConnectionStringSet":{"additionalProperties":false,"properties":{"action":{"enum":["set"],"type":"string","x-go-type":"string"},"value":{"maxLength":255,"type":"string"}},"required":["action","value"],"type":"object"},"CreateAskOnConnectAuthentication":{"additionalProperties":false,"properties":{"kind":{"enum":["ask_on_connect"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"CreateConnectionAuthentication":{"discriminator":{"mapping":{"ask_on_connect":"#/components/schemas/CreateAskOnConnectAuthentication","dedicated_password":"#/components/schemas/CreateDedicatedPasswordAuthentication","identity_file":"#/components/schemas/CreateIdentityFileAuthentication","new_shared_password":"#/components/schemas/CreateNewSharedPasswordAuthentication","saved_password":"#/components/schemas/CreateSavedPasswordAuthentication"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/CreateDedicatedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateSavedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateNewSharedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateIdentityFileAuthentication"},{"$ref":"#/components/schemas/CreateAskOnConnectAuthentication"}],"x-go-type":"json.RawMessage"},"CreateConnectionRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"authentication":{"$ref":"#/components/schemas/CreateConnectionAuthentication"},"group":{"maxLength":400,"type":"string"},"hostName":{"maxLength":255,"minLength":1,"type":"string"},"port":{"maximum":65535,"minimum":1,"type":"integer"},"proxyJump":{"maxLength":64,"minLength":1,"type":"string"},"user":{"maxLength":255,"type":"string"}},"required":["alias","hostName","authentication"],"type":"object"},"CreateConnectionResponse":{"additionalProperties":false,"properties":{"identity":{"$ref":"#/components/schemas/HostIdentity"},"preview":{"$ref":"#/components/schemas/SavePreview"},"transactionId":{"type":"string"}},"required":["transactionId","identity","preview"],"type":"object"},"CreateDedicatedPasswordAuthentication":{"additionalProperties":false,"properties":{"kind":{"enum":["dedicated_password"],"type":"string","x-go-type":"string"},"password":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["kind","password"],"type":"object"},"CreateIdentityFileAuthentication":{"additionalProperties":false,"properties":{"keyId":{"maxLength":32,"minLength":32,"type":"string"},"kind":{"enum":["identity_file"],"type":"string","x-go-type":"string"}},"required":["kind","keyId"],"type":"object"},"CreateNewSharedPasswordAuthentication":{"additionalProperties":false,"properties":{"credential":{"maxLength":128,"minLength":1,"type":"string"},"kind":{"enum":["new_shared_password"],"type":"string","x-go-type":"string"},"password":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["kind","credential","password"],"type":"object"},"CreateSavedPasswordAuthentication":{"additionalProperties":false,"properties":{"credential":{"maxLength":128,"minLength":1,"type":"string"},"kind":{"enum":["saved_password"],"type":"string","x-go-type":"string"}},"required":["kind","credential"],"type":"object"},"CreateTerminalWorkspaceRequest":{"additionalProperties":false,"properties":{"hidden":{"type":"boolean"},"name":{"maxLength":64,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"Credential":{"additionalProperties":false,"properties":{"hosts":{"items":{"type":"string"},"type":"array"},"kind":{"enum":["password","key_passphrase","totp"],"type":"string"},"name":{"type":"string"},"uses":{"items":{"type":"string"},"type":"array"}},"required":["kind","name","uses","hosts"],"type":"object"},"CredentialList":{"additionalProperties":false,"properties":{"credentials":{"items":{"$ref":"#/components/schemas/Credential"},"type":"array"},"dedicatedKeyPassphrases":{"items":{"$ref":"#/components/schemas/DedicatedKeyPassphraseUsage"},"type":"array"},"keyHostUsageComplete":{"type":"boolean"}},"required":["credentials","dedicatedKeyPassphrases","keyHostUsageComplete"],"type":"object"},"DedicatedKeyPassphraseUsage":{"additionalProperties":false,"properties":{"hosts":{"items":{"type":"string"},"type":"array"},"key":{"type":"string"}},"required":["key","hosts"],"type":"object"},"Diagnostic":{"additionalProperties":false,"properties":{"absolute":{"type":"string"},"code":{"type":"string"},"detail":{"type":"string"},"external":{"type":"boolean"},"line":{"type":"integer"},"path":{"type":"string"},"severity":{"type":"string"}},"required":["severity","code"],"type":"object"},"DiffLine":{"additionalProperties":false,"properties":{"newLine":{"type":"integer"},"oldLine":{"type":"integer"},"op":{"type":"string"},"text":{"type":"string"}},"required":["op","text"],"type":"object"},"EditRequest":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"base":{"type":"string"},"bases":{"description":"The file each move_hosts or delete_hosts target lives in, with the contents the client loaded, once per file.","items":{"$ref":"#/components/schemas/FileBase"},"maxItems":256,"minItems":1,"type":"array"},"comment":{"type":"string"},"destinationBase":{"type":"string"},"destinationGroup":{"maxLength":400,"type":"string"},"destinationPath":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/FieldEdit"},"type":"array"},"groups":{"items":{"$ref":"#/components/schemas/GroupMetadata"},"maxItems":256,"type":"array"},"groupsBase":{"items":{"$ref":"#/components/schemas/GroupMetadata"},"maxItems":256,"type":"array"},"hostMetadata":{"$ref":"#/components/schemas/HostMetadata"},"hostMetadataBase":{"$ref":"#/components/schemas/HostMetadata"},"hosts":{"description":"Targets for move_hosts or delete_hosts. Each path must have exactly one entry in bases.","items":{"$ref":"#/components/schemas/HostIdentity"},"maxItems":256,"minItems":1,"type":"array"},"kind":{"type":"string"},"newAlias":{"type":"string"},"path":{"type":"string"},"raw":{"type":"string"}},"required":["kind"],"type":"object"},"Effective":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"entries":{"items":{"$ref":"#/components/schemas/EffectiveEntry"},"type":"array"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"}},"required":["alias","entries"],"type":"object"},"EffectiveChange":{"additionalProperties":false,"properties":{"after":{"items":{"type":"string"},"type":"array"},"afterSources":{"items":{"$ref":"#/components/schemas/Source"},"type":"array"},"before":{"items":{"type":"string"},"type":"array"},"beforeSources":{"items":{"$ref":"#/components/schemas/Source"},"type":"array"},"keyword":{"type":"string"}},"required":["keyword","before","after"],"type":"object"},"EffectiveDiff":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"changes":{"items":{"$ref":"#/components/schemas/EffectiveChange"},"type":"array"}},"required":["alias","changes"],"type":"object"},"EffectiveEntry":{"additionalProperties":false,"properties":{"keyword":{"type":"string"},"source":{"$ref":"#/components/schemas/Source"},"values":{"items":{"type":"string"},"type":"array"}},"required":["keyword","values","source"],"type":"object"},"EffectiveResponse":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"complexities":{"items":{"$ref":"#/components/schemas/ComplexityNote"},"type":"array"},"executableDirectives":{"items":{"$ref":"#/components/schemas/ExecutableDirective"},"type":"array"},"route":{"items":{"$ref":"#/components/schemas/JumpStage"},"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/ValueSource"},"type":"array"}},"required":["alias","executableDirectives","sources","complexities","route"],"type":"object"},"EmbeddedTerminal":{"additionalProperties":false,"properties":{"appearance":{"$ref":"#/components/schemas/TerminalAppearance"},"browserScrollbackLines":{"maximum":100000,"minimum":1000,"type":"integer"},"commandMarks":{"type":"boolean"},"commandScrollbarMarks":{"type":"boolean"},"composerEnterSends":{"type":"boolean"},"copyOnSelect":{"type":"boolean"},"fontSize":{"maximum":32,"minimum":8,"type":"integer"},"jisYenBackslash":{"type":"boolean"},"ligatures":{"type":"boolean"},"localShellProfile":{"pattern":"^[a-z0-9-]{1,64}$","type":"string"},"maxSessions":{"maximum":200,"minimum":1,"type":"integer"},"retainedTerminals":{"maximum":99,"minimum":1,"type":"integer"},"rightClickPaste":{"type":"boolean"},"runningCommandTitle":{"type":"boolean"},"scrollbackBytes":{"maximum":4194304,"minimum":16384,"type":"integer"},"startDirectory":{"maxLength":4096,"type":"string"},"statusLine":{"type":"boolean"},"stickyCommand":{"type":"boolean"},"webgl":{"type":"boolean"}},"type":"object"},"ExecutableDirective":{"additionalProperties":false,"properties":{"command":{"type":"string"},"condition":{"type":"string"},"keyword":{"type":"string"},"line":{"type":"integer"},"onConnect":{"type":"boolean"},"overridable":{"type":"boolean"},"path":{"type":"string"}},"required":["keyword","command","path","line","condition","onConnect","overridable"],"type":"object"},"FieldEdit":{"additionalProperties":false,"properties":{"action":{"type":"string"},"keyword":{"type":"string"},"line":{"type":"integer"},"values":{"items":{"type":"string"},"type":"array"}},"required":["action"],"type":"object"},"FileBase":{"additionalProperties":false,"properties":{"base":{"type":"string"},"path":{"type":"string"}},"required":["path","base"],"type":"object"},"FileContents":{"additionalProperties":false,"properties":{"contents":{"type":"string"},"digest":{"type":"string"},"editable":{"type":"boolean"},"exists":{"type":"boolean"},"file":{"$ref":"#/components/schemas/FileRef"}},"required":["file","contents","digest","editable","exists"],"type":"object"},"FileDiff":{"additionalProperties":false,"properties":{"created":{"type":"boolean"},"lines":{"items":{"$ref":"#/components/schemas/DiffLine"},"type":"array"},"newDigest":{"type":"string"},"oldDigest":{"type":"string"},"path":{"type":"string"},"removed":{"type":"boolean"},"truncated":{"type":"boolean"}},"required":["path","lines"],"type":"object"},"FileNode":{"additionalProperties":false,"properties":{"editable":{"type":"boolean"},"file":{"$ref":"#/components/schemas/FileRef"},"includes":{"items":{"$ref":"#/components/schemas/IncludeReference"},"type":"array"},"loads":{"type":"integer"},"missing":{"type":"boolean"}},"required":["file","editable","loads"],"type":"object"},"FileRef":{"additionalProperties":false,"properties":{"absolute":{"type":"string"},"external":{"type":"boolean"},"path":{"type":"string"}},"required":["absolute"],"type":"object"},"FileTransferSettings":{"additionalProperties":false,"properties":{"excludePatterns":{"description":"Folder contents to exclude. Basenames match at any depth; patterns containing / start at the selected folder. Supports * and ? only. Defaults to no exclusions.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"maxConcurrent":{"maximum":8,"minimum":1,"type":"integer"},"speedLimitBytesPerSecond":{"format":"int64","maximum":1073741824,"minimum":0,"type":"integer"}},"type":"object"},"FormField":{"additionalProperties":false,"properties":{"category":{"type":"string"},"dangerous":{"type":"boolean"},"duplicate":{"type":"boolean"},"editable":{"type":"boolean"},"keyword":{"type":"string"},"line":{"type":"integer"},"values":{"items":{"type":"string"},"type":"array"}},"required":["line","keyword","values","category","editable"],"type":"object"},"GenerateKeyRequest":{"additionalProperties":false,"properties":{"algorithm":{"maxLength":16,"minLength":1,"type":"string"},"bits":{"type":"integer"},"comment":{"maxLength":127,"type":"string"},"fileName":{"maxLength":64,"minLength":1,"type":"string"},"group":{"maxLength":400,"type":"string"},"passphrase":{"maxLength":1024,"type":"string"},"unencrypted":{"type":"boolean"}},"required":["algorithm","fileName","comment","passphrase","unencrypted"],"type":"object"},"GenerateKeyResponse":{"additionalProperties":false,"properties":{"bits":{"type":"integer"},"encrypted":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"keyType":{"type":"string"},"publicRelativePath":{"type":"string"},"relativePath":{"type":"string"},"transactionId":{"type":"string"}},"required":["id","relativePath","publicRelativePath","fingerprint","keyType","bits","encrypted","transactionId"],"type":"object"},"GlobalFileTransferSettings":{"additionalProperties":false,"properties":{"autoReconnect":{"type":"boolean"},"clearCompletedAfterSeconds":{"maximum":86400,"minimum":0,"type":"integer"},"maxReconnectAttempts":{"maximum":10,"minimum":0,"type":"integer"}},"type":"object"},"GlobalSettings":{"additionalProperties":false,"properties":{"backgrounds":{"$ref":"#/components/schemas/BackgroundSettings"},"fileTransfers":{"$ref":"#/components/schemas/GlobalFileTransferSettings"},"language":{"enum":["en","ja"],"type":"string"},"terminal":{"$ref":"#/components/schemas/GlobalTerminalSettings"},"updateNoticeFoldedRelease":{"pattern":"^[0-9A-Za-z.+-]{1,64}$","type":"string"}},"type":"object"},"GlobalTerminalSettings":{"additionalProperties":false,"properties":{"osc52":{"type":"boolean"},"reconnect":{"maximum":5,"minimum":0,"type":"integer"},"sessionLog":{"type":"boolean"},"sessionLogRules":{"maxLength":8192,"type":"string"},"verbosity":{"maximum":3,"minimum":0,"type":"integer"}},"type":"object"},"GroupDeleteRequest":{"additionalProperties":false,"properties":{"destination":{"maxLength":400,"type":"string"},"name":{"maxLength":400,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"GroupMetadata":{"additionalProperties":false,"properties":{"colour":{"type":"string"},"hidden":{"type":"boolean"},"name":{"type":"string"},"order":{"type":"integer"},"settings":{"items":{"$ref":"#/components/schemas/Setting"},"type":"array"}},"required":["name"],"type":"object"},"GroupRenameRequest":{"additionalProperties":false,"properties":{"from":{"maxLength":400,"minLength":1,"type":"string"},"to":{"maxLength":400,"minLength":1,"type":"string"}},"required":["from","to"],"type":"object"},"GroupView":{"additionalProperties":false,"properties":{"colour":{"type":"string"},"directory":{"type":"string"},"directoryPresent":{"type":"boolean"},"keyDirectory":{"type":"string"},"memberCount":{"type":"integer"},"name":{"type":"string"},"order":{"type":"integer"},"parent":{"type":"string"},"settings":{"items":{"$ref":"#/components/schemas/Setting"},"type":"array"}},"required":["name","directory","keyDirectory","memberCount","directoryPresent"],"type":"object"},"HardwareCommandRequest":{"additionalProperties":false,"properties":{"algorithm":{"maxLength":16,"minLength":1,"type":"string"},"comment":{"maxLength":127,"type":"string"},"fileName":{"maxLength":64,"minLength":1,"type":"string"},"group":{"maxLength":400,"type":"string"}},"required":["algorithm","fileName","comment"],"type":"object"},"HardwareCommandResponse":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"command":{"items":{"type":"string"},"type":"array"},"note":{"type":"string"}},"required":["algorithm","command","note"],"type":"object"},"HealthResponse":{"additionalProperties":false,"properties":{"status":{"enum":["ok"],"type":"string","x-go-type":"string"},"version":{"minLength":1,"type":"string"}},"required":["status","version"],"type":"object"},"HistoryEntry":{"additionalProperties":false,"properties":{"finishedAt":{"type":"string"},"id":{"type":"string"},"operation":{"type":"string"},"paths":{"items":{"type":"string"},"type":"array"},"restorable":{"items":{"type":"string"},"type":"array"},"startedAt":{"type":"string"},"status":{"type":"string"}},"required":["id","operation","status","startedAt","paths"],"type":"object"},"HistoryList":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/HistoryEntry"},"type":"array"}},"required":["entries"],"type":"object"},"HostDetail":{"additionalProperties":false,"properties":{"effective":{"$ref":"#/components/schemas/Effective"},"file":{"$ref":"#/components/schemas/FileContents"},"form":{"$ref":"#/components/schemas/HostForm"},"metadata":{"$ref":"#/components/schemas/HostMetadata"}},"required":["form","metadata","effective","file"],"type":"object"},"HostEntry":{"additionalProperties":false,"properties":{"duplicate":{"type":"boolean"},"editable":{"type":"boolean"},"file":{"$ref":"#/components/schemas/FileRef"},"group":{"type":"string"},"hostName":{"description":"Resolved SSH destination hostname used by this alias.","type":"string"},"identity":{"$ref":"#/components/schemas/HostIdentity"},"line":{"type":"integer"},"negated":{"type":"boolean"},"patterns":{"items":{"type":"string"},"type":"array"},"port":{"description":"Resolved SSH port used by this alias.","type":"string"},"proxyJump":{"description":"Resolved ProxyJump of this alias, as ssh would use it (comma-separated hops). Absent when the alias connects directly.","type":"string"},"user":{"description":"Resolved remote account used by this alias.","type":"string"},"wildcard":{"type":"boolean"}},"required":["identity","file","line","patterns","editable"],"type":"object"},"HostForm":{"additionalProperties":false,"properties":{"comment":{"description":"The comment lines attached above the Host line, with the '#' markers stripped. Empty when the block has none. ssh_config has no trailing comment syntax, so only whole lines are ever attached.","type":"string"},"commentLines":{"description":"How many physical lines the attached comment occupies. A client rewriting the whole file needs the count to include those lines; it cannot be derived from comment, whose markers and indentation were stripped.","type":"integer"},"entry":{"$ref":"#/components/schemas/HostEntry"},"fields":{"items":{"$ref":"#/components/schemas/FormField"},"type":"array"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"raw":{"type":"string"}},"required":["entry","fields","raw","comment","commentLines"],"type":"object"},"HostIdentity":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"path":{"maxLength":512,"type":"string"}},"required":["path","alias"],"type":"object"},"HostMetadata":{"additionalProperties":false,"properties":{"appearance":{"$ref":"#/components/schemas/TerminalAppearance"},"colour":{"type":"string"},"detectedOS":{"description":"Last operating system detected after connecting.","enum":["","server","linux","amazonlinux","ubuntu","debian","redhat","fedora","centos","rocky","almalinux","arch","alpine","opensuse","freebsd","macos","windows"],"type":"string"},"detectedOSBinding":{"maxLength":64,"type":"string"},"encoding":{"description":"Text encoding used by this SSH terminal. Omitted means UTF-8.","enum":["utf-8","shift_jis","euc-jp","iso-2022-jp"],"type":"string"},"identity":{"$ref":"#/components/schemas/HostIdentity"},"order":{"type":"integer"},"orphan":{"type":"boolean"},"os":{"description":"Home icon override. Empty or omitted enables automatic detection.","enum":["","server","linux","amazonlinux","ubuntu","debian","redhat","fedora","centos","rocky","almalinux","arch","alpine","opensuse","freebsd","macos","windows"],"type":"string"},"passwordSaveDeclinedBinding":{"maxLength":64,"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"tmux":{"type":"boolean"},"vpn":{"maxLength":48,"type":"string"}},"required":["identity"],"type":"object"},"IKEv2Profile":{"additionalProperties":false,"properties":{"authentication":{"maxLength":16,"type":"string"},"caCertificate":{"maxLength":16384,"type":"string"},"esp":{"maxLength":256,"type":"string"},"identity":{"maxLength":256,"minLength":1,"type":"string"},"ike":{"maxLength":256,"type":"string"},"server":{"maxLength":320,"minLength":1,"type":"string"},"serverIdentity":{"maxLength":256,"type":"string"}},"required":["server","authentication","identity"],"type":"object"},"IncludeReference":{"additionalProperties":false,"properties":{"condition":{"type":"string"},"line":{"type":"integer"},"matches":{"items":{"$ref":"#/components/schemas/FileRef"},"type":"array"},"pattern":{"type":"string"}},"required":["line","pattern"],"type":"object"},"InterfaceSettings":{"additionalProperties":false,"properties":{"hideSFTPThumbnails":{"type":"boolean"},"hideTouchLinkUnderlines":{"type":"boolean"},"keepScreenAwake":{"type":"boolean"},"keyBarPreset":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"notificationSound":{"$ref":"#/components/schemas/NotificationSound"},"shortcutPreset":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"touchMode":{"enum":["touch","mouse"],"type":"string"}},"type":"object"},"InterfaceState":{"additionalProperties":false,"properties":{"collapsedTerminalWorkspaces":{"maxLength":65536,"type":"string"},"runTarget":{"maxLength":65536,"type":"string"},"sftpPanes":{"maxLength":65536,"type":"string"},"sftpQueueView":{"maxLength":65536,"type":"string"},"showHiddenTerminalWorkspaces":{"maxLength":65536,"type":"string"}},"type":"object"},"IssueActionRequest":{"additionalProperties":false,"properties":{"kind":{"maxLength":64,"minLength":1,"type":"string"},"target":{"maxLength":49152,"minLength":1,"type":"string"}},"required":["kind","target"],"type":"object"},"IssueActionResponse":{"additionalProperties":false,"properties":{"expiresAt":{"minLength":1,"type":"string"},"token":{"maxLength":43,"minLength":43,"type":"string"}},"required":["token","expiresAt"],"type":"object"},"JumpStage":{"additionalProperties":false,"properties":{"complex":{"type":"boolean"},"depth":{"type":"integer"},"hop":{"type":"string"},"hostname":{"type":"string"},"order":{"type":"integer"},"parent":{"type":"string"},"port":{"type":"string"},"user":{"type":"string"}},"required":["order","depth","parent","hop","hostname","user","port","complex"],"type":"object"},"KeyAlgorithmsResponse":{"additionalProperties":false,"properties":{"source":{"type":"string"},"variants":{"items":{"$ref":"#/components/schemas/KeyVariant"},"type":"array"}},"required":["variants","source"],"type":"object"},"KeyBarKey":{"additionalProperties":false,"properties":{"enter":{"type":"boolean"},"key":{"enum":["DirectInput","Control","Alt","Escape","Tab","Shift+Tab","Enter","Backspace","Delete","Insert","ArrowLeft","ArrowUp","ArrowDown","ArrowRight","Home","End","PageUp","PageDown","Ctrl+C","Ctrl+D","Ctrl+Z","Ctrl+L","Ctrl+R","Ctrl+A","Ctrl+E","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12"],"type":"string"},"label":{"maxLength":8,"minLength":1,"type":"string"},"text":{"maxLength":1000,"minLength":1,"type":"string"}},"type":"object"},"KeyBarPreset":{"additionalProperties":false,"properties":{"id":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"keys":{"items":{"$ref":"#/components/schemas/KeyBarKey"},"maxItems":16,"type":"array"},"moreKeys":{"items":{"$ref":"#/components/schemas/KeyBarKey"},"maxItems":48,"type":"array"},"name":{"maxLength":80,"minLength":1,"type":"string"}},"required":["id","name","keys","moreKeys"],"type":"object"},"KeyCertificate":{"additionalProperties":false,"properties":{"keyId":{"type":"string"},"neverExpires":{"type":"boolean"},"principals":{"items":{"type":"string"},"type":"array"},"signedKeyFingerprint":{"type":"string"},"signedKeyType":{"type":"string"},"validBefore":{"type":"integer"}},"required":["keyId","principals","validBefore","neverExpires","signedKeyType","signedKeyFingerprint"],"type":"object"},"KeyInventoryResponse":{"additionalProperties":false,"properties":{"agentAvailable":{"type":"boolean"},"agentDelegations":{"items":{"$ref":"#/components/schemas/KeyReference"},"type":"array"},"agentIdentities":{"items":{"$ref":"#/components/schemas/AgentIdentity"},"type":"array"},"items":{"items":{"$ref":"#/components/schemas/KeyItem"},"type":"array"},"unreadable":{"items":{"$ref":"#/components/schemas/UnreadableFile"},"type":"array"},"unresolvedReferences":{"items":{"$ref":"#/components/schemas/UnresolvedReference"},"type":"array"}},"required":["items","unreadable","agentDelegations","unresolvedReferences","agentAvailable","agentIdentities"],"type":"object"},"KeyItem":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"bits":{"type":"integer"},"certificate":{"$ref":"#/components/schemas/KeyCertificate"},"comment":{"type":"string"},"container":{"type":"string"},"encrypted":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"keyType":{"type":"string"},"kind":{"type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"permission":{"type":"string"},"permissionRisk":{"type":"boolean"},"references":{"items":{"$ref":"#/components/schemas/KeyReference"},"type":"array"},"relativePath":{"type":"string"}},"required":["id","relativePath","kind","container","algorithm","keyType","bits","encrypted","fingerprint","comment","permission","permissionRisk","references","notes"],"type":"object"},"KeyReference":{"additionalProperties":false,"properties":{"condition":{"type":"string"},"configPath":{"type":"string"},"directive":{"type":"string"},"hostPatterns":{"items":{"type":"string"},"type":"array"},"line":{"type":"integer"},"value":{"type":"string"}},"required":["directive","configPath","line","condition","hostPatterns","value"],"type":"object"},"KeyVariant":{"additionalProperties":false,"properties":{"algorithm":{"type":"string"},"bits":{"type":"integer"},"inProcess":{"type":"boolean"},"label":{"type":"string"},"reason":{"type":"string"}},"required":["algorithm","bits","label","inProcess","reason"],"type":"object"},"KnownHostCandidate":{"additionalProperties":false,"properties":{"fingerprint":{"type":"string"},"host":{"type":"string"},"key":{"type":"string"},"keyType":{"type":"string"},"port":{"type":"integer"},"verified":{"type":"boolean"}},"required":["host","port","keyType","key","fingerprint","verified"],"type":"object"},"KnownHostEntry":{"additionalProperties":false,"properties":{"aliases":{"description":"The saved connections whose key this entry holds, matched by the name ssh looks the key up under (HostKeyAlias, or HostName and Port). A hashed entry is matched by hashing that name. Absent when the entry holds no saved connection's key.","items":{"type":"string"},"type":"array"},"comment":{"type":"string"},"digest":{"type":"string"},"fingerprint":{"type":"string"},"hashed":{"type":"boolean"},"hosts":{"items":{"type":"string"},"type":"array"},"keyType":{"type":"string"},"line":{"type":"integer"},"marker":{"type":"string"}},"required":["line","digest","marker","hosts","hashed","keyType","fingerprint","comment"],"type":"object"},"KnownHostTarget":{"additionalProperties":false,"properties":{"digest":{"maxLength":64,"minLength":64,"type":"string"},"line":{"type":"integer"}},"required":["line","digest"],"type":"object"},"KnownHostsAddRequest":{"additionalProperties":false,"properties":{"acknowledged":{"type":"boolean"},"expectedFingerprint":{"maxLength":128,"type":"string"},"host":{"maxLength":255,"minLength":1,"type":"string"},"key":{"maxLength":4096,"minLength":1,"type":"string"},"keyType":{"maxLength":64,"minLength":1,"type":"string"},"port":{"type":"integer"}},"required":["host","port","keyType","key","expectedFingerprint","acknowledged"],"type":"object"},"KnownHostsChangeResponse":{"additionalProperties":false,"properties":{"changed":{"type":"boolean"},"transactionId":{"type":"string"}},"required":["changed","transactionId"],"type":"object"},"KnownHostsDeleteRequest":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/KnownHostTarget"},"type":"array"}},"required":["entries"],"type":"object"},"KnownHostsResponse":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/KnownHostEntry"},"type":"array"},"path":{"type":"string"}},"required":["path","entries"],"type":"object"},"KnownHostsScanRequest":{"additionalProperties":false,"properties":{"host":{"maxLength":255,"minLength":1,"type":"string"},"port":{"type":"integer"}},"required":["host","port"],"type":"object"},"KnownHostsScanResponse":{"additionalProperties":false,"properties":{"candidates":{"items":{"$ref":"#/components/schemas/KnownHostCandidate"},"type":"array"},"notice":{"type":"string"}},"required":["notice","candidates"],"type":"object"},"L2TPProfile":{"additionalProperties":false,"properties":{"esp":{"maxLength":256,"type":"string"},"ike":{"maxLength":256,"type":"string"},"server":{"maxLength":320,"minLength":1,"type":"string"},"username":{"maxLength":256,"minLength":1,"type":"string"}},"required":["server","username"],"type":"object"},"LocalShellProfile":{"additionalProperties":false,"properties":{"arguments":{"items":{"type":"string"},"type":"array"},"default":{"type":"boolean"},"id":{"type":"string"},"label":{"type":"string"},"path":{"type":"string"}},"required":["id","label","path","arguments","default"],"type":"object"},"LocalShellProfileList":{"additionalProperties":false,"properties":{"profiles":{"items":{"$ref":"#/components/schemas/LocalShellProfile"},"type":"array"}},"required":["profiles"],"type":"object"},"Machine":{"additionalProperties":false,"properties":{"facts":{"$ref":"#/components/schemas/MachineFacts"},"id":{"pattern":"^[0-9a-f]{32}$","type":"string"},"settings":{"$ref":"#/components/schemas/MachineSettings"}},"required":["id","settings","facts"],"type":"object"},"MachineFacts":{"additionalProperties":false,"properties":{"os":{"type":"string"},"sessionLogDirectory":{"type":"string"},"startedAt":{"format":"date-time","type":"string"},"version":{"type":"string"}},"type":"object"},"MachineSettings":{"additionalProperties":false,"properties":{"name":{"maxLength":80,"type":"string"},"port":{"maximum":65535,"minimum":1024,"type":"integer"},"presetId":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"sessionLogDirectory":{"maxLength":4096,"type":"string"}},"type":"object"},"MachineState":{"additionalProperties":false,"properties":{"facts":{"$ref":"#/components/schemas/MachineFacts"},"interface":{"$ref":"#/components/schemas/InterfaceState"},"transfersStopped":{"type":"boolean"}},"required":["facts"],"type":"object"},"MachineStatus":{"additionalProperties":false,"properties":{"cpuPercent":{"maximum":100,"minimum":0,"type":"number"},"hostname":{"maxLength":255,"type":"string"},"memoryTotalBytes":{"format":"int64","minimum":0,"type":"integer"},"memoryUsedBytes":{"format":"int64","minimum":0,"type":"integer"},"os":{"maxLength":32,"minLength":1,"type":"string"},"uptimeSeconds":{"format":"int64","minimum":0,"type":"integer"}},"required":["hostname","os"],"type":"object"},"Metadata":{"additionalProperties":false,"properties":{"groups":{"items":{"$ref":"#/components/schemas/GroupMetadata"},"type":"array"},"groupsFile":{"type":"string"},"hosts":{"items":{"$ref":"#/components/schemas/HostMetadata"},"type":"array"},"keyBarPresets":{"items":{"$ref":"#/components/schemas/KeyBarPreset"},"maxItems":64,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"},"schemaVersion":{"type":"integer"},"settings":{"$ref":"#/components/schemas/GlobalSettings"},"shortcutPresets":{"items":{"$ref":"#/components/schemas/ShortcutPreset"},"maxItems":64,"type":"array"},"vpnProfiles":{"items":{"$ref":"#/components/schemas/VPNProfile"},"maxItems":64,"type":"array"}},"required":["schemaVersion","presets"],"type":"object"},"MoveTerminalSessionRequest":{"additionalProperties":false,"properties":{"workspaceId":{"maxLength":64,"minLength":1,"type":"string"}},"required":["workspaceId"],"type":"object"},"MoveTerminalWorkspaceRequest":{"additionalProperties":false,"properties":{"beforeId":{"maxLength":64,"minLength":1,"type":"string"}},"type":"object"},"Notice":{"additionalProperties":false,"properties":{"code":{"type":"string"},"detail":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"},"related":{"$ref":"#/components/schemas/NoticeLocation"}},"required":["code"],"type":"object"},"NoticeLocation":{"additionalProperties":false,"properties":{"line":{"type":"integer"},"path":{"type":"string"}},"required":["path","line"],"type":"object"},"NotificationSound":{"additionalProperties":false,"properties":{"sound":{"enum":["none","gentle","bell","pulse"],"type":"string"},"volume":{"maximum":100,"minimum":0,"type":"integer"}},"required":["sound","volume"],"type":"object"},"OpenConnectProfile":{"additionalProperties":false,"properties":{"approvalWord":{"maxLength":32,"type":"string"},"protocol":{"maxLength":32,"type":"string"},"secondFactor":{"maxLength":16,"type":"string"},"server":{"maxLength":320,"minLength":1,"type":"string"},"serverCertificate":{"maxLength":128,"type":"string"},"username":{"maxLength":256,"minLength":1,"type":"string"}},"required":["server","username"],"type":"object"},"OpenTerminalSessionRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"cols":{"maximum":1000,"minimum":1,"type":"integer"},"cwd":{"maxLength":4096,"minLength":1,"type":"string"},"destination":{"maxLength":519,"minLength":1,"type":"string"},"kind":{"enum":["ssh","shell"],"type":"string"},"profileId":{"pattern":"^[a-z0-9-]{1,64}$","type":"string"},"rows":{"maximum":1000,"minimum":1,"type":"integer"},"workspaceId":{"maxLength":64,"minLength":1,"type":"string"}},"required":["kind"],"type":"object"},"OpenTerminalSessionResponse":{"additionalProperties":false,"properties":{"session":{"$ref":"#/components/schemas/TerminalSession"},"streamTicket":{"type":"string"}},"required":["session","streamTicket"],"type":"object"},"OpenVPNProfile":{"additionalProperties":false,"properties":{"servers":{"items":{"maxLength":320,"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"},"username":{"maxLength":256,"type":"string"}},"required":["servers"],"type":"object"},"Overview":{"additionalProperties":false,"properties":{"diagnostics":{"items":{"$ref":"#/components/schemas/Diagnostic"},"type":"array"},"entry":{"$ref":"#/components/schemas/FileRef"},"files":{"items":{"$ref":"#/components/schemas/FileNode"},"type":"array"},"groups":{"items":{"$ref":"#/components/schemas/GroupView"},"type":"array"},"hosts":{"items":{"$ref":"#/components/schemas/HostEntry"},"type":"array"},"metadata":{"$ref":"#/components/schemas/Metadata"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"pending":{"items":{"$ref":"#/components/schemas/PendingTransaction"},"type":"array"}},"required":["entry","files","hosts","groups","metadata","diagnostics","notices"],"type":"object"},"PassphraseRequest":{"additionalProperties":false,"properties":{"passphrase":{"maxLength":1024,"type":"string"}},"required":["passphrase"],"type":"object"},"PasswordEligibility":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"blockers":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"hostName":{"type":"string"},"passwordBinding":{"enum":["unavailable","none","current","stale"],"type":"string"},"port":{"type":"string"},"storable":{"type":"boolean"},"totpBinding":{"enum":["unavailable","none","current","stale"],"type":"string"},"warnings":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"}},"required":["alias","storable","blockers","warnings"],"type":"object"},"PasswordVaultStatus":{"additionalProperties":false,"properties":{"aliases":{"items":{"type":"string"},"type":"array"},"dedicatedKeyPassphrases":{"items":{"type":"string"},"type":"array"},"exists":{"type":"boolean"},"migratedFromVersion":{"type":"integer"},"migratedToVersion":{"type":"integer"},"minPassphraseLength":{"type":"integer"},"passwordless":{"type":"boolean"},"unlocked":{"type":"boolean"}},"required":["exists","unlocked","aliases","dedicatedKeyPassphrases"],"type":"object"},"PendingChanges":{"additionalProperties":false,"properties":{"files":{"items":{"$ref":"#/components/schemas/PendingFileChange"},"type":"array"},"id":{"type":"string"}},"required":["id","files"],"type":"object"},"PendingFileChange":{"additionalProperties":false,"properties":{"action":{"enum":["write","move","remove","mkdir","rmdir","note"],"type":"string","x-go-type":"string"},"after":{"type":"string"},"afterExists":{"type":"boolean"},"before":{"type":"string"},"beforeExists":{"type":"boolean"},"committed":{"type":"boolean"},"contents":{"enum":["text","none","secret","binary","too_large","vault_locked","unavailable"],"type":"string","x-go-type":"string"},"path":{"type":"string"},"target":{"type":"string"}},"required":["path","action","committed","contents","beforeExists","afterExists"],"type":"object"},"PendingTransaction":{"additionalProperties":false,"properties":{"canComplete":{"type":"boolean"},"committed":{"type":"integer"},"id":{"type":"string"},"operation":{"type":"string"},"paths":{"items":{"type":"string"},"type":"array"},"startedAt":{"type":"string"},"status":{"type":"string"}},"required":["id","operation","status","startedAt","committed","paths","canComplete"],"type":"object"},"Problem":{"additionalProperties":false,"properties":{"blockers":{"items":{"type":"string"},"type":"array"},"code":{"type":"string"},"column":{"type":"integer"},"conflict":{"$ref":"#/components/schemas/ConflictReport"},"currentVersion":{"minimum":0,"type":"integer"},"detail":{"type":"string"},"diagnostics":{"items":{"$ref":"#/components/schemas/Diagnostic"},"type":"array"},"directive":{"type":"string"},"field":{"type":"string"},"limit":{"minimum":1,"type":"integer"},"line":{"type":"integer"},"message":{"type":"string"},"path":{"type":"string"},"reason":{"type":"string"},"requiredVersion":{"minimum":1,"type":"integer"}},"required":["code","message"],"type":"object"},"PublicKeyResponse":{"additionalProperties":false,"properties":{"comment":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"publicKey":{"type":"string"},"relativePath":{"type":"string"}},"required":["id","relativePath","publicKey","fingerprint","comment"],"type":"object"},"PullRequest":{"additionalProperties":false,"properties":{"acceptRemoteHead":{"type":"boolean"},"apply":{"type":"boolean"},"expectedETag":{"maxLength":1024,"type":"string"},"expectedRevision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"historyKey":{"maxLength":1024,"minLength":1,"type":"string"},"resolve":{"enum":["local","remote"],"type":"string"}},"type":"object"},"PullResponse":{"additionalProperties":false,"properties":{"added":{"items":{"type":"string"},"type":"array"},"applied":{"type":"boolean"},"completedAt":{"type":"string"},"conflicts":{"items":{"$ref":"#/components/schemas/SyncConflict"},"type":"array"},"downloadedBytes":{"format":"int64","minimum":0,"type":"integer"},"origin":{"type":"string"},"remoteETag":{"type":"string"},"remoteRevision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"removed":{"items":{"type":"string"},"type":"array"},"summary":{"$ref":"#/components/schemas/SnapshotSummary"},"written":{"items":{"type":"string"},"type":"array"}},"required":["applied","conflicts","written","added","removed","summary","downloadedBytes","completedAt","remoteETag","remoteRevision"],"type":"object"},"PurgeTrashResponse":{"additionalProperties":false,"properties":{"entryId":{"type":"string"},"removed":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["entryId","removed","transactionId"],"type":"object"},"PushResponse":{"additionalProperties":false,"properties":{"result":{"$ref":"#/components/schemas/PushResult"},"status":{"$ref":"#/components/schemas/SyncStatus"}},"required":["status","result"],"type":"object"},"PushResult":{"additionalProperties":false,"properties":{"added":{"items":{"type":"string"},"type":"array"},"completedAt":{"type":"string"},"modified":{"items":{"type":"string"},"type":"array"},"objectCount":{"minimum":0,"type":"integer"},"removed":{"items":{"type":"string"},"type":"array"},"summary":{"$ref":"#/components/schemas/SnapshotSummary"},"uploadedBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["summary","objectCount","uploadedBytes","completedAt","added","modified","removed"],"type":"object"},"ReachabilityResponse":{"additionalProperties":false,"properties":{"address":{"type":"string"},"detail":{"type":"string"},"notice":{"type":"string"},"outcome":{"type":"string"}},"required":["address","outcome","detail","notice"],"type":"object"},"RecentConnection":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hostName":{"type":"string"},"lastConnectedAt":{"format":"date-time","type":"string"},"port":{"type":"string"},"user":{"type":"string"}},"required":["alias","hostName","user","port","lastConnectedAt"],"type":"object"},"RecentConnectionList":{"additionalProperties":false,"properties":{"connections":{"items":{"$ref":"#/components/schemas/RecentConnection"},"maxItems":20,"type":"array"}},"required":["connections"],"type":"object"},"RecoverRequest":{"additionalProperties":false,"properties":{"action":{"maxLength":16,"minLength":1,"type":"string"},"transactionId":{"maxLength":128,"minLength":1,"type":"string"}},"required":["transactionId","action"],"type":"object"},"RecoverResponse":{"additionalProperties":false,"properties":{"status":{"enum":["ok"],"type":"string","x-go-type":"string"}},"required":["status"],"type":"object"},"RegisterKeyRequest":{"additionalProperties":false,"properties":{"lifetimeSeconds":{"type":"integer"},"passphrase":{"maxLength":1024,"type":"string"}},"required":["passphrase","lifetimeSeconds"],"type":"object"},"RegisterKeyResponse":{"additionalProperties":false,"properties":{"fingerprint":{"type":"string"},"id":{"type":"string"},"identities":{"items":{"$ref":"#/components/schemas/AgentIdentity"},"type":"array"},"lifetimeSeconds":{"type":"integer"},"relativePath":{"type":"string"}},"required":["id","relativePath","fingerprint","lifetimeSeconds","identities"],"type":"object"},"ReleaseNotes":{"additionalProperties":false,"properties":{"body":{"description":"Original Markdown displayed as plain text; bounded to 64 KiB of UTF-8.","maxLength":65536,"type":"string"},"pageUrl":{"type":"string"},"publishedAt":{"format":"date-time","type":"string"},"version":{"type":"string"}},"required":["version","pageUrl","body"],"type":"object"},"RelocateKeyRequest":{"additionalProperties":false,"properties":{"group":{"maxLength":400,"type":"string"},"newName":{"maxLength":64,"minLength":1,"type":"string"}},"type":"object"},"RelocateKeyResponse":{"additionalProperties":false,"properties":{"blockers":{"items":{"type":"string"},"type":"array"},"files":{"items":{"$ref":"#/components/schemas/RelocatedKeyFile"},"type":"array"},"group":{"type":"string"},"id":{"type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/components/schemas/RewrittenKeyReference"},"type":"array"},"relativePath":{"type":"string"},"skipped":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["id","relativePath","group","files","references","skipped","notes","blockers","transactionId"],"type":"object"},"RelocatedKeyFile":{"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"RemoteEngine":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"connected":{"type":"boolean"}},"required":["alias","connected"],"type":"object"},"RemoteEngineAction":{"enum":["stop","restart","update"],"type":"string","x-enum-varnames":["RemoteEngineActionStop","RemoteEngineActionRestart","RemoteEngineActionUpdate"]},"RemoteEngineActionLog":{"additionalProperties":false,"properties":{"action":{"$ref":"#/components/schemas/RemoteEngineAction"},"cursor":{"$ref":"#/components/schemas/TerminalControlCursor"},"done":{"type":"boolean"},"generation":{"format":"uint64","minimum":1,"type":"integer"},"output":{"maxLength":65536,"type":"string"},"problem":{"type":"string"},"succeeded":{"type":"boolean"}},"required":["generation","action","done","succeeded","cursor","output"],"type":"object"},"RemoteEngineActionRequest":{"additionalProperties":false,"properties":{"action":{"$ref":"#/components/schemas/RemoteEngineAction"}},"required":["action"],"type":"object"},"RemoteEngineActionStarted":{"additionalProperties":false,"properties":{"generation":{"format":"uint64","minimum":1,"type":"integer"}},"required":["generation"],"type":"object"},"RemoteEngineEntrance":{"additionalProperties":false,"properties":{"url":{"type":"string"}},"required":["url"],"type":"object"},"RemoteEngineList":{"additionalProperties":false,"properties":{"engines":{"items":{"$ref":"#/components/schemas/RemoteEngine"},"type":"array"}},"required":["engines"],"type":"object"},"RemoteEngineStatus":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"passwordless":{"type":"boolean"},"sessions":{"minimum":0,"type":"integer"},"unlocked":{"type":"boolean"},"vault":{"type":"boolean"},"version":{"type":"string"}},"required":["alias","version","vault","unlocked","passwordless","sessions"],"type":"object"},"RemoteKeyPlan":{"additionalProperties":false,"properties":{"actionToken":{"type":"string"},"alias":{"type":"string"},"executableDirectives":{"items":{"$ref":"#/components/schemas/ExecutableDirective"},"type":"array"},"fingerprint":{"type":"string"},"hostname":{"type":"string"},"keyLine":{"type":"string"},"keyPath":{"type":"string"},"manual":{"items":{"type":"string"},"type":"array"},"port":{"type":"string"},"remotePath":{"type":"string"},"routine":{"type":"string"},"supported":{"type":"boolean"},"user":{"type":"string"},"valuesFrom":{"type":"string"}},"required":["alias","user","hostname","port","valuesFrom","fingerprint","keyPath","keyLine","remotePath","routine","supported","manual","executableDirectives","actionToken"],"type":"object"},"RemoteKeyPlanRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"keyPath":{"maxLength":4096,"type":"string"},"publicKey":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["alias","keyPath","publicKey"],"type":"object"},"RemoteKeyRegisterRequest":{"additionalProperties":false,"properties":{"acknowledgeExecutable":{"type":"boolean"},"alias":{"maxLength":64,"minLength":1,"type":"string"},"keyPath":{"maxLength":4096,"type":"string"},"publicKey":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["alias","keyPath","publicKey","acknowledgeExecutable"],"type":"object"},"RemoteKeyRegisterResponse":{"additionalProperties":false,"properties":{"exitCode":{"type":"integer"},"outcome":{"type":"string"},"stderr":{"type":"string"},"truncated":{"type":"boolean"}},"required":["outcome","exitCode","stderr","truncated"],"type":"object"},"RenameTerminalBackgroundRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":128,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"ResetUnsupportedVaultRequest":{"additionalProperties":false,"properties":{"acknowledged":{"type":"boolean"},"passphrase":{"maxLength":1024,"type":"string"}},"required":["passphrase","acknowledged"],"type":"object"},"RestoreRequest":{"additionalProperties":false,"properties":{"path":{"maxLength":512,"minLength":1,"type":"string"},"transactionId":{"maxLength":128,"minLength":1,"type":"string"}},"required":["transactionId","path"],"type":"object"},"RestoreTrashResponse":{"additionalProperties":false,"properties":{"blockers":{"items":{"type":"string"},"type":"array"},"entryId":{"type":"string"},"restored":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["entryId","restored","blockers","transactionId"],"type":"object"},"RevealCredentialResponse":{"additionalProperties":false,"properties":{"kind":{"type":"string"},"name":{"type":"string"},"secret":{"type":"string"}},"required":["kind","name","secret"],"type":"object"},"RevealPrivateKeyResponse":{"additionalProperties":false,"properties":{"encrypted":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"privateKey":{"type":"string"},"relativePath":{"type":"string"},"transactionId":{"type":"string"}},"required":["id","relativePath","privateKey","encrypted","fingerprint","transactionId"],"type":"object"},"RewrittenKeyReference":{"additionalProperties":false,"properties":{"configPath":{"type":"string"},"directive":{"type":"string"},"from":{"type":"string"},"line":{"type":"integer"},"to":{"type":"string"}},"required":["directive","configPath","line","from","to"],"type":"object"},"SFTPChangeSymlinkRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"},"target":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","target","expectedRevision"],"type":"object"},"SFTPChmodEntry":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","expectedRevision"],"type":"object"},"SFTPChmodOptions":{"additionalProperties":false,"properties":{"directoryMode":{"pattern":"^0?[0-7]{3}$","type":"string"},"fileMode":{"pattern":"^0?[0-7]{3}$","type":"string"},"recursive":{"type":"boolean"}},"required":["fileMode","directoryMode","recursive"],"type":"object"},"SFTPChmodPlan":{"additionalProperties":false,"properties":{"actionToken":{"maxLength":43,"minLength":43,"type":"string"},"directories":{"minimum":0,"type":"integer"},"files":{"minimum":0,"type":"integer"},"options":{"$ref":"#/components/schemas/SFTPChmodOptions"},"revision":{"minLength":1,"type":"string"},"selectionCount":{"maximum":200,"minimum":1,"type":"integer"},"skippedSymlinks":{"minimum":0,"type":"integer"}},"required":["revision","selectionCount","files","directories","skippedSymlinks","options","actionToken"],"type":"object"},"SFTPChmodRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"mode":{"pattern":"^0?[0-7]{3}$","type":"string"},"path":{"type":"string"},"recursive":{"type":"boolean"}},"required":["path","mode","expectedRevision","recursive"],"type":"object"},"SFTPChmodResult":{"additionalProperties":false,"properties":{"applied":{"minimum":0,"type":"integer"},"complete":{"type":"boolean"},"items":{"minimum":1,"type":"integer"}},"required":["applied","items","complete"],"type":"object","x-sshc-less-than-or-equal":[["applied","items"]]},"SFTPChmodSelection":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPChmodEntry"},"maxItems":200,"minItems":1,"type":"array"},"options":{"$ref":"#/components/schemas/SFTPChmodOptions"}},"required":["entries","options"],"type":"object"},"SFTPChmodSelectionRequest":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPChmodEntry"},"maxItems":200,"minItems":1,"type":"array"},"expectedRevision":{"minLength":1,"type":"string"},"options":{"$ref":"#/components/schemas/SFTPChmodOptions"}},"required":["entries","options","expectedRevision"],"type":"object"},"SFTPComparisonMode":{"default":"metadata","enum":["metadata","content"],"type":"string"},"SFTPCompleteUploadRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"},"sourceFingerprint":{"pattern":"^tree-sha256:[0-9a-f]{64}$","type":"string"}},"required":["path","size","expectedRevision","sourceFingerprint"],"type":"object"},"SFTPConnectionAttempt":{"additionalProperties":false,"properties":{"finished":{"type":"boolean"},"hostKeyQuestion":{"$ref":"#/components/schemas/TerminalHostKeyQuestion"},"log":{"description":"Connection log lines at the configured verbosity, oldest first, without line endings. A failed attempt ends with the reason.","items":{"type":"string"},"type":"array"},"progress":{"$ref":"#/components/schemas/TerminalConnectionProgress"}},"required":["log","finished"],"type":"object"},"SFTPContentMatch":{"additionalProperties":false,"properties":{"entry":{"$ref":"#/components/schemas/SFTPEntry"},"line":{"minimum":1,"type":"integer"},"snippet":{"maxLength":162,"type":"string"}},"required":["entry","line","snippet"],"type":"object"},"SFTPCreateEntryRequest":{"additionalProperties":false,"properties":{"path":{"type":"string"},"type":{"enum":["directory","file"],"type":"string"}},"required":["path","type"],"type":"object"},"SFTPCreateSymlinkRequest":{"additionalProperties":false,"properties":{"path":{"maxLength":4096,"minLength":1,"type":"string"},"target":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","target"],"type":"object"},"SFTPCreateTransferJobRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"batchId":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"batchKind":{"enum":["file","folder"],"type":"string"},"batchName":{"maxLength":1024,"minLength":1,"type":"string"},"direction":{"enum":["upload","download","remote"],"type":"string"},"id":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"kind":{"enum":["file","folder"],"type":"string"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"lastModified":{"format":"int64","minimum":0,"type":"integer"},"name":{"maxLength":1024,"minLength":1,"type":"string"},"operation":{"enum":["","copy","move","delete","get","put"],"type":"string"},"overwrite":{"type":"boolean"},"remotePath":{"minLength":1,"type":"string"},"sourceAlias":{"maxLength":64,"type":"string"},"sourcePath":{"type":"string"},"totalBytes":{"format":"int64","minimum":-1,"type":"integer"}},"required":["id","batchId","batchName","batchKind","alias","sourceAlias","sourcePath","operation","overwrite","direction","kind","name","remotePath","totalBytes","lastModified"],"type":"object"},"SFTPDirectoryComparison":{"additionalProperties":false,"properties":{"bytesRead":{"format":"int64","maximum":268435456,"minimum":0,"type":"integer"},"entries":{"items":{"$ref":"#/components/schemas/SFTPDirectoryDifference"},"maxItems":20000,"type":"array"},"leftPath":{"type":"string"},"mode":{"$ref":"#/components/schemas/SFTPComparisonMode"},"rightPath":{"type":"string"},"truncated":{"type":"boolean"}},"required":["leftPath","rightPath","entries"],"type":"object"},"SFTPDirectoryDifference":{"additionalProperties":false,"properties":{"left":{"$ref":"#/components/schemas/SFTPEntry"},"omission":{"description":"byte_limit or unsupported; the content was not compared","type":"string"},"relativePath":{"type":"string"},"right":{"$ref":"#/components/schemas/SFTPEntry"},"status":{"enum":["same","different","left_only","right_only","type_mismatch","unverified"],"type":"string"}},"required":["relativePath","status"],"type":"object"},"SFTPDirectoryStats":{"additionalProperties":false,"properties":{"bytes":{"format":"int64","minimum":0,"type":"integer"},"directories":{"minimum":1,"type":"integer"},"files":{"minimum":0,"type":"integer"},"path":{"type":"string"},"truncated":{"type":"boolean"}},"required":["path","bytes","files","directories","truncated"],"type":"object"},"SFTPDownloadCheckpointRequest":{"additionalProperties":false,"properties":{"offset":{"format":"int64","minimum":0,"type":"integer"},"revision":{"maxLength":256,"minLength":1,"type":"string"}},"required":["offset","revision"],"type":"object"},"SFTPDownloadPartProgress":{"additionalProperties":false,"properties":{"index":{"maximum":127,"minimum":0,"type":"integer"},"totalBytes":{"format":"int64","minimum":0,"type":"integer"},"transferredBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["index","transferredBytes","totalBytes"],"type":"object","x-sshc-less-than-or-equal":[["transferredBytes","totalBytes"]]},"SFTPEntry":{"additionalProperties":false,"properties":{"gid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer"},"linkTarget":{"type":"string"},"mode":{"type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"path":{"type":"string"},"revision":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"},"targetType":{"enum":["file","directory","other"],"type":"string"},"type":{"enum":["file","directory","symlink","other"],"type":"string"},"uid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer"}},"required":["name","path","type","size","mode","modifiedAt","revision"],"type":"object"},"SFTPFilesystemSpace":{"additionalProperties":false,"properties":{"availableBytes":{"description":"Bytes available to the authenticated account as an exact uint64 decimal string.","pattern":"^(0|[1-9][0-9]{0,19})$","type":"string"},"path":{"type":"string"},"totalBytes":{"description":"Total filesystem bytes as an exact uint64 decimal string.","pattern":"^(0|[1-9][0-9]{0,19})$","type":"string"}},"required":["path","availableBytes","totalBytes"],"type":"object"},"SFTPListing":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPEntry"},"type":"array"},"path":{"type":"string"}},"required":["path","entries"],"type":"object"},"SFTPLocalDeleteEntry":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","expectedRevision"],"type":"object"},"SFTPLocalDeletePlan":{"additionalProperties":false,"properties":{"actionToken":{"minLength":1,"type":"string"},"items":{"format":"int64","minimum":1,"type":"integer"},"revision":{"minLength":1,"type":"string"}},"required":["revision","items","actionToken"],"type":"object"},"SFTPLocalDeleteRequest":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPLocalDeleteEntry"},"maxItems":200,"minItems":1,"type":"array"},"expectedRevision":{"minLength":1,"type":"string"}},"required":["entries","expectedRevision"],"type":"object"},"SFTPLocalDeleteSelection":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPLocalDeleteEntry"},"maxItems":200,"minItems":1,"type":"array"}},"required":["entries"],"type":"object"},"SFTPLocalListing":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/SFTPEntry"},"type":"array"},"home":{"type":"string"},"path":{"type":"string"}},"required":["path","home","entries"],"type":"object"},"SFTPLocalMkdirRequest":{"additionalProperties":false,"properties":{"directory":{"maxLength":4096,"minLength":1,"type":"string"},"name":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["directory","name"],"type":"object"},"SFTPLocalRenameRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"name":{"maxLength":1024,"minLength":1,"type":"string"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","name","expectedRevision"],"type":"object"},"SFTPMediaTicket":{"additionalProperties":false,"properties":{"ticket":{"minLength":1,"type":"string"}},"required":["ticket"],"type":"object"},"SFTPMediaTicketRequest":{"additionalProperties":false,"properties":{"alias":{"description":"An SSH alias, or sshc://local for the engine's own disk","minLength":1,"type":"string"},"path":{"minLength":1,"type":"string"}},"required":["alias","path"],"type":"object"},"SFTPOwnershipRequest":{"additionalProperties":false,"properties":{"expectedRevision":{"minLength":1,"type":"string"},"gid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer","x-go-type":"*int64"},"path":{"maxLength":4096,"minLength":1,"type":"string"},"uid":{"format":"int64","maximum":4294967295,"minimum":0,"type":"integer","x-go-type":"*int64"}},"required":["path","uid","gid","expectedRevision"],"type":"object"},"SFTPRenameRequest":{"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"SFTPResumableUpload":{"additionalProperties":false,"properties":{"chunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"completedRanges":{"items":{"$ref":"#/components/schemas/SFTPUploadRange"},"maxItems":65536,"type":"array"},"expectedRevision":{"type":"string"},"id":{"type":"string"},"offset":{"format":"int64","minimum":0,"type":"integer"},"parallelism":{"maximum":128,"minimum":1,"type":"integer"},"path":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"}},"required":["id","path","offset","size","expectedRevision","completedRanges","parallelism","chunkBytes"],"type":"object"},"SFTPSaveTextRequest":{"additionalProperties":false,"properties":{"contents":{"maxLength":2097152,"type":"string"},"expectedRevision":{"minLength":1,"type":"string"}},"required":["contents","expectedRevision"],"type":"object"},"SFTPSearchOmission":{"additionalProperties":false,"properties":{"count":{"minimum":1,"type":"integer"},"reason":{"description":"symlink, unsupported, binary, file_size, unreadable, changed, byte_limit, result_limit, entry_limit or depth_limit","type":"string"}},"required":["reason","count"],"type":"object"},"SFTPSearchResult":{"additionalProperties":false,"properties":{"bytesRead":{"format":"int64","maximum":67108864,"minimum":0,"type":"integer"},"entries":{"items":{"$ref":"#/components/schemas/SFTPEntry"},"maxItems":200,"type":"array"},"matches":{"items":{"$ref":"#/components/schemas/SFTPContentMatch"},"maxItems":200,"type":"array"},"omissions":{"items":{"$ref":"#/components/schemas/SFTPSearchOmission"},"maxItems":10,"type":"array"},"path":{"type":"string"},"query":{"type":"string"},"truncated":{"type":"boolean"}},"required":["path","query","truncated","entries"],"type":"object"},"SFTPStartUploadRequest":{"additionalProperties":false,"properties":{"path":{"type":"string"},"size":{"format":"int64","maximum":549755813888,"minimum":0,"type":"integer"},"sourceFingerprint":{"pattern":"^tree-sha256:[0-9a-f]{64}$","type":"string"}},"required":["path","size","sourceFingerprint"],"type":"object"},"SFTPTextFile":{"additionalProperties":false,"properties":{"contents":{"maxLength":2097152,"type":"string"},"entry":{"$ref":"#/components/schemas/SFTPEntry"},"revision":{"type":"string"}},"required":["entry","contents","revision"],"type":"object"},"SFTPTransfer":{"additionalProperties":false,"properties":{"bytes":{"format":"int64","minimum":0,"type":"integer"},"path":{"type":"string"},"revision":{"type":"string"}},"required":["path","bytes","revision"],"type":"object"},"SFTPTransferJob":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"allowedActions":{"items":{"enum":["pause","resume","retry","cancel","remove"],"type":"string"},"maxItems":3,"type":"array","uniqueItems":true},"attempt":{"minimum":1,"type":"integer"},"batchId":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"batchKind":{"enum":["file","folder"],"type":"string"},"batchName":{"maxLength":1024,"minLength":1,"type":"string"},"bytesPerSecond":{"format":"double","minimum":0,"type":"number"},"createdAt":{"format":"date-time","type":"string"},"direction":{"enum":["upload","download","remote"],"type":"string"},"downloadParts":{"items":{"$ref":"#/components/schemas/SFTPDownloadPartProgress"},"maxItems":128,"type":"array"},"downloadRevision":{"maxLength":256,"type":"string"},"excludePatterns":{"description":"Exclusions captured when this folder job was queued.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"expectedRevision":{"maxLength":256,"type":"string"},"id":{"pattern":"^[A-Za-z0-9_-]{8,128}$","type":"string"},"kind":{"enum":["file","folder"],"type":"string"},"lastModified":{"format":"int64","minimum":0,"type":"integer"},"name":{"maxLength":1024,"minLength":1,"type":"string"},"operation":{"enum":["","copy","move","delete","get","put"],"type":"string"},"overwrite":{"type":"boolean"},"problem":{"maxLength":128,"type":"string"},"reconnectAt":{"description":"Next reconnect time, or empty while not waiting.","type":"string"},"reconnectAttempt":{"maximum":10,"minimum":0,"type":"integer"},"remainingSeconds":{"format":"int64","minimum":-1,"type":"integer"},"remotePath":{"minLength":1,"type":"string"},"sourceAlias":{"maxLength":64,"type":"string"},"sourceFingerprint":{"maxLength":128,"type":"string"},"sourcePath":{"type":"string"},"status":{"enum":["queued","running","reconnecting","paused","reattach","needs_overwrite","completed","failed","cancelled"],"type":"string"},"totalBytes":{"format":"int64","minimum":-1,"type":"integer"},"transferredBytes":{"format":"int64","minimum":0,"type":"integer"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","batchId","batchName","batchKind","alias","sourceAlias","sourcePath","operation","direction","kind","name","remotePath","totalBytes","transferredBytes","bytesPerSecond","remainingSeconds","status","allowedActions","attempt","reconnectAttempt","reconnectAt","problem","lastModified","expectedRevision","sourceFingerprint","overwrite","downloadRevision","downloadParts","createdAt","updatedAt"],"type":"object"},"SFTPTransferJobActionRequest":{"additionalProperties":false,"properties":{"action":{"enum":["start","reconnect","pause","resume","retry","cancel","progress","complete","fail","needs_overwrite"],"type":"string"},"problem":{"maxLength":128,"type":"string"},"resetProgress":{"type":"boolean"},"totalBytes":{"format":"int64","minimum":0,"type":"integer"},"transferredBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["action"],"type":"object"},"SFTPTransferJobList":{"additionalProperties":false,"properties":{"autoReconnect":{"type":"boolean"},"clearCompletedAfterSeconds":{"maximum":86400,"minimum":0,"type":"integer"},"excludePatterns":{"description":"Folder contents to exclude. Basenames match at any depth; patterns containing / start at the selected folder. Supports * and ? only. Defaults to no exclusions.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"jobs":{"items":{"$ref":"#/components/schemas/SFTPTransferJob"},"maxItems":200,"type":"array"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"maxConcurrent":{"maximum":8,"minimum":1,"type":"integer"},"maxReconnectAttempts":{"maximum":10,"minimum":0,"type":"integer"},"processingStopped":{"type":"boolean"},"speedLimitBytesPerSecond":{"format":"int64","maximum":1073741824,"minimum":0,"type":"integer"}},"required":["maxConcurrent","clearCompletedAfterSeconds","processingStopped","largeFileThresholdBytes","largeFileParallelism","largeFileChunkBytes","speedLimitBytesPerSecond","autoReconnect","maxReconnectAttempts","jobs"],"type":"object"},"SFTPTransferQueueMoveRequest":{"additionalProperties":false,"properties":{"move":{"enum":["up","down","top","bottom"],"type":"string"}},"required":["move"],"type":"object"},"SFTPTransferSettingsRequest":{"additionalProperties":false,"properties":{"autoReconnect":{"type":"boolean"},"clearCompletedAfterSeconds":{"maximum":86400,"minimum":0,"type":"integer"},"excludePatterns":{"description":"Folder contents to exclude. Basenames match at any depth; patterns containing / start at the selected folder. Supports * and ? only. Defaults to no exclusions.","items":{"maxLength":512,"minLength":1,"type":"string"},"maxItems":64,"type":"array"},"largeFileChunkBytes":{"format":"int64","maximum":4294967296,"minimum":8388608,"type":"integer"},"largeFileParallelism":{"maximum":128,"minimum":1,"type":"integer"},"largeFileThresholdBytes":{"format":"int64","maximum":1073741824,"minimum":16777216,"type":"integer"},"maxConcurrent":{"maximum":8,"minimum":1,"type":"integer"},"maxReconnectAttempts":{"maximum":10,"minimum":0,"type":"integer"},"processingStopped":{"type":"boolean"},"speedLimitBytesPerSecond":{"format":"int64","maximum":1073741824,"minimum":0,"type":"integer"}},"required":["maxConcurrent","clearCompletedAfterSeconds","processingStopped","largeFileThresholdBytes","largeFileParallelism","largeFileChunkBytes","speedLimitBytesPerSecond","autoReconnect","maxReconnectAttempts"],"type":"object"},"SFTPUploadRange":{"additionalProperties":false,"properties":{"offset":{"format":"int64","minimum":0,"type":"integer"},"size":{"format":"int64","maximum":4294967296,"minimum":1,"type":"integer"}},"required":["offset","size"],"type":"object"},"SavePreview":{"additionalProperties":false,"properties":{"diffs":{"items":{"$ref":"#/components/schemas/FileDiff"},"type":"array"},"effective":{"items":{"$ref":"#/components/schemas/EffectiveDiff"},"type":"array"},"notices":{"items":{"$ref":"#/components/schemas/Notice"},"type":"array"},"operation":{"type":"string"}},"required":["operation","diffs"],"type":"object"},"SaveResult":{"additionalProperties":false,"properties":{"preview":{"$ref":"#/components/schemas/SavePreview"},"transactionId":{"type":"string"},"written":{"items":{"type":"string"},"type":"array"}},"required":["transactionId","written","preview"],"type":"object"},"SetTerminalSessionTitleRequest":{"additionalProperties":false,"properties":{"title":{"maxLength":64,"minLength":1,"nullable":true,"type":"string"}},"required":["title"],"type":"object"},"Setting":{"additionalProperties":false,"properties":{"keyword":{"type":"string"},"values":{"description":"The arguments of the directive. ProxyCommand, RemoteCommand, LocalCommand and KnownHostsCommand take the rest of the line as one value, written without requoting; a request with more than one value for them is refused.","items":{"type":"string"},"type":"array"}},"required":["keyword","values"],"type":"object"},"SettingsDocument":{"additionalProperties":false,"properties":{"global":{"$ref":"#/components/schemas/GlobalSettings"},"machineId":{"type":"string"},"machines":{"items":{"$ref":"#/components/schemas/Machine"},"type":"array"},"presetInUse":{"type":"string"},"presets":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"},"state":{"$ref":"#/components/schemas/MachineState"}},"required":["machineId","global","presets","presetInUse","machines","state"],"type":"object"},"SettingsPreset":{"additionalProperties":false,"properties":{"appearance":{"$ref":"#/components/schemas/AppearanceSettings"},"fileTransfers":{"$ref":"#/components/schemas/FileTransferSettings"},"id":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"interface":{"$ref":"#/components/schemas/InterfaceSettings"},"name":{"maxLength":80,"minLength":1,"type":"string"},"terminal":{"$ref":"#/components/schemas/EmbeddedTerminal"},"vaultAutoLock":{"$ref":"#/components/schemas/VaultAutoLockSettings"}},"required":["id"],"type":"object"},"SettingsSaveResult":{"additionalProperties":false,"properties":{"transactionId":{"type":"string"},"written":{"items":{"type":"string"},"type":"array"}},"required":["transactionId","written"],"type":"object"},"ShortcutPreset":{"additionalProperties":false,"properties":{"bindings":{"additionalProperties":false,"properties":{"closeSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"copy":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"decreaseFontSize":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"home":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"increaseFontSize":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"newSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"nextPane":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"nextPrompt":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"nextSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"palette":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"paste":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"previousPane":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"previousPrompt":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"previousSession":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"quickCommands":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"resetFontSize":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"sftp":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"splitBelow":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"splitRight":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"terminalSearch":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"},"toggleNavigation":{"items":{"maxLength":64,"type":"string"},"maxItems":3,"type":"array"}},"required":["palette","terminalSearch","copy","paste","previousPrompt","nextPrompt","increaseFontSize","decreaseFontSize","resetFontSize","quickCommands","newSession","closeSession","nextSession","previousSession","nextPane","previousPane","splitRight","splitBelow","toggleNavigation","home","sftp"],"type":"object"},"id":{"pattern":"^[a-zA-Z0-9-]{1,64}$","type":"string"},"name":{"maxLength":80,"minLength":1,"type":"string"}},"required":["id","name","bindings"],"type":"object"},"SnapshotSummary":{"additionalProperties":false,"properties":{"createdAt":{"type":"string"},"fileCount":{"minimum":0,"type":"integer"},"snapshotBytes":{"format":"int64","minimum":0,"type":"integer"},"sourceBytes":{"format":"int64","minimum":0,"type":"integer"}},"required":["createdAt","fileCount","sourceBytes","snapshotBytes"],"type":"object"},"Snippet":{"additionalProperties":false,"properties":{"command":{"description":"The command, or empty for a snippet of steps.","maxLength":65536,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"description":{"maxLength":2048,"type":"string"},"id":{"type":"string"},"name":{"maxLength":96,"minLength":1,"type":"string"},"steps":{"items":{"$ref":"#/components/schemas/SnippetStep"},"maxItems":32,"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"variables":{"items":{"$ref":"#/components/schemas/SnippetVariable"},"maxItems":32,"type":"array"},"waitSeconds":{"maximum":300,"minimum":0,"type":"integer"}},"required":["name","description","command","variables","id","createdAt","updatedAt"],"type":"object"},"SnippetDraft":{"additionalProperties":false,"properties":{"command":{"description":"The command, or empty for a snippet of steps.","maxLength":65536,"type":"string"},"description":{"maxLength":2048,"type":"string"},"name":{"maxLength":96,"minLength":1,"type":"string"},"steps":{"items":{"$ref":"#/components/schemas/SnippetStep"},"maxItems":32,"type":"array"},"variables":{"items":{"$ref":"#/components/schemas/SnippetVariable"},"maxItems":32,"type":"array"},"waitSeconds":{"description":"How long each step waits for its text; 0 is the default of 10 seconds.","maximum":300,"minimum":0,"type":"integer"}},"required":["name","description","command","variables"],"type":"object"},"SnippetExecuteRequest":{"additionalProperties":false,"description":"Exactly one of snippetId or command and exactly one of aliases or targets must be supplied. targets permits repeated aliases when targetId values are distinct.","properties":{"aliases":{"items":{"type":"string"},"maxItems":64,"minItems":1,"type":"array"},"command":{"maxLength":65536,"minLength":1,"type":"string"},"concurrency":{"maximum":8,"minimum":1,"type":"integer"},"evidence":{"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/SnippetExecutionTarget"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs","evidence","concurrency"],"type":"object","x-sshc-exactly-one":[["snippetId","command"],["aliases","targets"]]},"SnippetExecutionTarget":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"targetId":{"maxLength":255,"minLength":1,"type":"string"}},"required":["targetId","alias"],"type":"object"},"SnippetJob":{"additionalProperties":false,"properties":{"finishedAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"results":{"items":{"$ref":"#/components/schemas/SnippetTargetResult"},"type":"array"},"startedAt":{"format":"date-time","type":"string"},"status":{"enum":["running","completed","cancelled"],"type":"string"}},"required":["id","status","startedAt","results"],"type":"object"},"SnippetLibrary":{"additionalProperties":false,"properties":{"snippets":{"items":{"$ref":"#/components/schemas/Snippet"},"type":"array"},"startup":{"items":{"$ref":"#/components/schemas/StartupSnippet"},"type":"array"}},"required":["snippets","startup"],"type":"object"},"SnippetPreview":{"additionalProperties":false,"properties":{"actionExpiresAt":{"format":"date-time","type":"string"},"actionToken":{"type":"string"},"evidence":{"type":"string"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/SnippetPreviewTarget"},"type":"array"}},"required":["snippetId","evidence","targets","actionToken","actionExpiresAt"],"type":"object"},"SnippetPreviewRequest":{"additionalProperties":false,"description":"Exactly one of snippetId or command and exactly one of aliases or targets must be supplied. targets permits repeated aliases when targetId values are distinct.","properties":{"aliases":{"items":{"type":"string"},"maxItems":64,"minItems":1,"type":"array"},"command":{"maxLength":65536,"minLength":1,"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/SnippetExecutionTarget"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs"],"type":"object","x-sshc-exactly-one":[["snippetId","command"],["aliases","targets"]]},"SnippetPreviewTarget":{"additionalProperties":false,"properties":{"command":{"type":"string"},"target":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hostName":{"type":"string"},"port":{"type":"string"},"route":{"items":{"properties":{"alias":{"type":"string"},"authentication":{"items":{"type":"string"},"type":"array"},"hostKeyAlgorithms":{"items":{"type":"string"},"type":"array"},"hostName":{"type":"string"},"identitiesOnly":{"type":"boolean"},"identityFiles":{"items":{"type":"string"},"type":"array"},"port":{"type":"string"},"proxyCommand":{"type":"string"},"strictHostKey":{"type":"string"},"user":{"type":"string"}},"required":["alias","hostName","user","port","strictHostKey","authentication"],"type":"object"},"type":"array"},"user":{"type":"string"}},"required":["alias","hostName","user","port"],"type":"object"},"targetId":{"type":"string"}},"required":["targetId","target","command"],"type":"object"},"SnippetStep":{"additionalProperties":false,"description":"One step of a snippet of steps. It waits for wait in the shell's output when wait is given, then types send, with Enter after it when enter is true.","properties":{"enter":{"type":"boolean"},"optional":{"description":"A step whose text does not come in time is passed over instead of stopping the steps.","type":"boolean"},"send":{"maxLength":4095,"type":"string"},"wait":{"maxLength":256,"type":"string"}},"required":["send","enter"],"type":"object"},"SnippetTargetResult":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"exitCode":{"type":"integer"},"problem":{"type":"string"},"status":{"enum":["queued","running","succeeded","failed","cancelled"],"type":"string"},"stderr":{"type":"string"},"stdout":{"type":"string"},"targetId":{"type":"string"},"truncated":{"type":"boolean"}},"required":["targetId","alias","status"],"type":"object"},"SnippetVariable":{"additionalProperties":false,"properties":{"default":{"type":"string"},"description":{"type":"string"},"name":{"type":"string"},"required":{"type":"boolean"},"type":{"enum":["string","integer","boolean","secret"],"type":"string"}},"required":["name","type"],"type":"object"},"Source":{"additionalProperties":false,"properties":{"absolute":{"type":"string"},"condition":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"}},"type":"object"},"StartTerminalForwardRequest":{"additionalProperties":false,"properties":{"destination":{"maxLength":512,"type":"string"},"kind":{"pattern":"^(local|remote|dynamic)$","type":"string"},"listenPort":{"maximum":65535,"minimum":0,"type":"integer"}},"required":["kind","listenPort"],"type":"object"},"StartupSnippet":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"snippetId":{"type":"string"},"stale":{"description":"The host's destination changed after the assignment, so the snippet is not sent until it is assigned again.","type":"boolean"}},"required":["alias","snippetId","stale"],"type":"object"},"StartupSnippetRequest":{"additionalProperties":false,"properties":{"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"}},"required":["snippetId","inputs"],"type":"object"},"StoreCredentialRequest":{"additionalProperties":false,"properties":{"secret":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["secret"],"type":"object"},"SyncBucketObject":{"additionalProperties":false,"properties":{"key":{"minLength":1,"type":"string"},"lastModified":{"type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"}},"required":["key","size"],"type":"object"},"SyncBucketStatus":{"additionalProperties":false,"properties":{"checkedAt":{"type":"string"},"history":{"items":{"$ref":"#/components/schemas/SyncBucketObject"},"maxItems":10000,"type":"array"},"historyTruncated":{"type":"boolean"},"live":{"$ref":"#/components/schemas/SyncBucketObject"},"localIsLive":{"type":"boolean"}},"required":["checkedAt","localIsLive","history","historyTruncated"],"type":"object"},"SyncConflict":{"additionalProperties":false,"properties":{"baseMode":{"pattern":"^0[67]00$","type":"string"},"changedHere":{"type":"boolean"},"changedThere":{"type":"boolean"},"localMode":{"pattern":"^0[67]00$","type":"string"},"path":{"type":"string"},"remoteMode":{"pattern":"^0[67]00$","type":"string"}},"required":["path","changedHere","changedThere"],"type":"object"},"SyncDirection":{"enum":["both","push","pull"],"type":"string"},"SyncExclusionCandidate":{"additionalProperties":false,"properties":{"ignored":{"type":"boolean"},"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path","ignored"],"type":"object"},"SyncExclusions":{"additionalProperties":false,"properties":{"candidates":{"items":{"$ref":"#/components/schemas/SyncExclusionCandidate"},"maxItems":4096,"type":"array"},"document":{"maxLength":32768,"type":"string"},"usingDefaults":{"type":"boolean"}},"required":["document","usingDefaults","candidates"],"type":"object"},"SyncExclusionsRequest":{"additionalProperties":false,"properties":{"document":{"maxLength":32768,"type":"string"}},"required":["document"],"type":"object"},"SyncHistory":{"additionalProperties":false,"properties":{"checkedAt":{"type":"string"},"downloadTruncated":{"type":"boolean"},"downloadedBytes":{"format":"int64","maximum":134217728,"minimum":0,"type":"integer"},"historyTruncated":{"type":"boolean"},"revisions":{"items":{"$ref":"#/components/schemas/SyncHistoryRevision"},"maxItems":50,"type":"array"},"skipped":{"maximum":50,"minimum":0,"type":"integer"}},"required":["checkedAt","revisions","historyTruncated","downloadTruncated","downloadedBytes","skipped"],"type":"object"},"SyncHistoryDiff":{"additionalProperties":false,"properties":{"added":{"items":{"type":"string"},"type":"array"},"downloadedBytes":{"format":"int64","minimum":0,"type":"integer"},"modified":{"items":{"type":"string"},"type":"array"},"removed":{"items":{"type":"string"},"type":"array"}},"required":["added","modified","removed","downloadedBytes"],"type":"object"},"SyncHistoryDiffRequest":{"additionalProperties":false,"properties":{"key":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"SyncHistoryRelation":{"enum":["head","ancestor","branch"],"type":"string"},"SyncHistoryRevision":{"additionalProperties":false,"properties":{"createdAt":{"type":"string"},"fileCount":{"maximum":4096,"minimum":0,"type":"integer"},"key":{"maxLength":1024,"minLength":1,"type":"string"},"lastModified":{"type":"string"},"message":{"maxLength":240,"minLength":1,"type":"string"},"origin":{"type":"string"},"parentRevision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"relation":{"$ref":"#/components/schemas/SyncHistoryRelation"},"revision":{"pattern":"^[0-9a-f]{64}$","type":"string"},"size":{"format":"int64","minimum":0,"type":"integer"}},"required":["key","revision","message","createdAt","origin","fileCount","size","relation"],"type":"object"},"SyncKeyRequest":{"additionalProperties":false,"properties":{"confirmHistoryLoss":{"type":"boolean"},"key":{"maxLength":1024,"minLength":1,"type":"string"}},"type":"object"},"SyncKeyResponse":{"additionalProperties":false,"properties":{"key":{"type":"string"}},"required":["key"],"type":"object"},"SyncOperation":{"additionalProperties":false,"properties":{"completedAt":{"type":"string"},"downloadedBytes":{"format":"int64","minimum":0,"type":"integer"},"kind":{"$ref":"#/components/schemas/SyncOperationKind"},"objectCount":{"minimum":0,"type":"integer"},"removed":{"minimum":0,"type":"integer"},"summary":{"$ref":"#/components/schemas/SnapshotSummary"},"uploadedBytes":{"format":"int64","minimum":0,"type":"integer"},"written":{"minimum":0,"type":"integer"}},"required":["kind","summary","completedAt"],"type":"object"},"SyncOperationKind":{"enum":["push","apply"],"type":"string"},"SyncPushDraft":{"additionalProperties":false,"properties":{"added":{"maximum":4096,"minimum":0,"type":"integer"},"message":{"maxLength":240,"minLength":1,"type":"string"},"modified":{"maximum":4096,"minimum":0,"type":"integer"},"removed":{"maximum":4096,"minimum":0,"type":"integer"}},"required":["message","added","modified","removed"],"type":"object"},"SyncPushRequest":{"additionalProperties":false,"properties":{"message":{"maxLength":240,"minLength":1,"type":"string"}},"required":["message"],"type":"object"},"SyncSetupCheckRequest":{"additionalProperties":false,"properties":{"accessKeyId":{"maxLength":512,"minLength":1,"type":"string"},"bucket":{"maxLength":255,"minLength":1,"type":"string"},"endpoint":{"maxLength":2048,"minLength":1,"type":"string"},"path":{"maxLength":255,"type":"string"},"region":{"maxLength":64,"type":"string"},"reuseCredentials":{"type":"boolean"},"secretAccessKey":{"maxLength":512,"minLength":1,"type":"string"}},"required":["endpoint","bucket","reuseCredentials"],"type":"object"},"SyncSetupCheckResponse":{"additionalProperties":false,"properties":{"checkedAt":{"type":"string"},"etag":{"maxLength":1024,"minLength":1,"type":"string"},"historyPresent":{"type":"boolean"},"state":{"$ref":"#/components/schemas/SyncSetupTargetState"}},"required":["state","historyPresent","checkedAt"],"type":"object"},"SyncSetupExport":{"additionalProperties":false,"properties":{"document":{"maxLength":16384,"minLength":1,"type":"string"},"keyGenerated":{"type":"boolean"},"qrRows":{"items":{"maxLength":177,"minLength":21,"pattern":"^[01]+$","type":"string"},"maxItems":177,"minItems":21,"type":"array"}},"required":["document","keyGenerated"],"type":"object"},"SyncSetupImportRequest":{"additionalProperties":false,"properties":{"direction":{"$ref":"#/components/schemas/SyncDirection"},"document":{"maxLength":16384,"minLength":1,"type":"string"},"key":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["document","key","direction"],"type":"object"},"SyncSetupRequest":{"additionalProperties":false,"properties":{"accessKeyId":{"maxLength":512,"minLength":1,"type":"string"},"bucket":{"maxLength":255,"minLength":1,"type":"string"},"direction":{"$ref":"#/components/schemas/SyncDirection"},"endpoint":{"maxLength":2048,"minLength":1,"type":"string"},"expectedETag":{"maxLength":1024,"minLength":1,"type":"string"},"expectedState":{"$ref":"#/components/schemas/SyncSetupTargetState"},"historyPresent":{"type":"boolean"},"key":{"maxLength":1024,"type":"string"},"path":{"maxLength":255,"type":"string"},"region":{"maxLength":64,"type":"string"},"reuseCredentials":{"type":"boolean"},"reuseKey":{"type":"boolean"},"secretAccessKey":{"maxLength":512,"minLength":1,"type":"string"}},"required":["endpoint","bucket","direction","expectedState","historyPresent","key","reuseCredentials","reuseKey"],"type":"object"},"SyncSetupResponse":{"additionalProperties":false,"properties":{"generatedKey":{"minLength":1,"type":"string"},"status":{"$ref":"#/components/schemas/SyncStatus"}},"required":["status"],"type":"object"},"SyncSetupTargetState":{"enum":["empty","existing","incomplete"],"type":"string"},"SyncStatus":{"additionalProperties":false,"properties":{"accessKeySuffix":{"maxLength":5,"type":"string"},"auto":{"$ref":"#/components/schemas/AutoSync"},"bucket":{"type":"string"},"configured":{"type":"boolean"},"direction":{"$ref":"#/components/schemas/SyncDirection"},"endpoint":{"type":"string"},"fileCount":{"type":"integer"},"keyConfigured":{"type":"boolean"},"lastOperation":{"$ref":"#/components/schemas/SyncOperation"},"lastSyncedAt":{"type":"string"},"locked":{"type":"boolean"},"origin":{"type":"string"},"path":{"type":"string"},"region":{"type":"string"},"synced":{"type":"boolean"}},"required":["configured","locked","endpoint","bucket","synced","direction","keyConfigured","auto"],"type":"object"},"TOTPCodeSet":{"additionalProperties":false,"properties":{"current":{"pattern":"^[0-9]{6,8}$","type":"string"},"next":{"pattern":"^[0-9]{6,8}$","type":"string"},"periodSeconds":{"maximum":300,"minimum":5,"type":"integer"},"previous":{"pattern":"^[0-9]{6,8}$","type":"string"},"remainingSeconds":{"maximum":300,"minimum":1,"type":"integer"}},"required":["previous","current","next","periodSeconds","remainingSeconds"],"type":"object"},"TerminalAppearance":{"additionalProperties":false,"properties":{"background":{"maxLength":128,"type":"string"},"backgroundTint":{"maximum":100,"minimum":0,"type":"integer"},"font":{"maxLength":64,"type":"string"},"palette":{"maxLength":64,"type":"string"}},"type":"object"},"TerminalBackground":{"additionalProperties":false,"properties":{"bytes":{"type":"integer"},"name":{"type":"string"},"type":{"type":"string"}},"required":["name","bytes","type"],"type":"object"},"TerminalBackgroundCapacityRequest":{"additionalProperties":false,"properties":{"capacityMiB":{"maximum":1024,"minimum":1,"type":"integer"}},"required":["capacityMiB"],"type":"object"},"TerminalBackgroundList":{"additionalProperties":false,"properties":{"backgrounds":{"items":{"$ref":"#/components/schemas/TerminalBackground"},"type":"array"},"capacityBytes":{"type":"integer"},"remainingBytes":{"type":"integer"},"usedBytes":{"type":"integer"}},"required":["backgrounds","usedBytes","capacityBytes","remainingBytes"],"type":"object"},"TerminalCommandDispatchRequest":{"additionalProperties":false,"properties":{"command":{"maxLength":4095,"minLength":1,"type":"string"},"evidence":{"maxLength":64,"minLength":64,"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"snippetId":{"type":"string"},"submit":{"description":"Send Enter after the command. Defaults to true.","type":"boolean"},"targets":{"items":{"$ref":"#/components/schemas/TerminalCommandTargetRequest"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs","targets","evidence"],"type":"object"},"TerminalCommandDispatchResponse":{"additionalProperties":false,"properties":{"results":{"items":{"$ref":"#/components/schemas/TerminalCommandResult"},"type":"array"}},"required":["results"],"type":"object"},"TerminalCommandPreview":{"additionalProperties":false,"properties":{"actionExpiresAt":{"type":"string"},"actionToken":{"type":"string"},"evidence":{"type":"string"},"reviewEvidence":{"description":"Stable evidence for the displayed redacted command and exact targets, independent of whether Enter will be sent.","type":"string"},"snippetId":{"type":"string"},"targets":{"items":{"$ref":"#/components/schemas/TerminalCommandPreviewTarget"},"type":"array"}},"required":["snippetId","evidence","reviewEvidence","actionToken","actionExpiresAt","targets"],"type":"object"},"TerminalCommandPreviewRequest":{"additionalProperties":false,"properties":{"command":{"maxLength":4095,"minLength":1,"type":"string"},"expectedReviewEvidence":{"description":"Refuse to issue an action if the redacted command or exact target changed since the displayed passive preview.","maxLength":64,"minLength":64,"type":"string"},"inputs":{"additionalProperties":{"type":"string"},"type":"object"},"issueAction":{"description":"Issue a one-time execution token. Defaults to true; passive previews should set false.","type":"boolean"},"revealCommand":{"description":"Return the expanded command for an explicit insert, run, or copy action. Normal previews remain redacted.","type":"boolean"},"snippetId":{"type":"string"},"submit":{"description":"Bind the preview to sending Enter after the command. Defaults to true.","type":"boolean"},"targets":{"items":{"$ref":"#/components/schemas/TerminalCommandTargetRequest"},"maxItems":64,"minItems":1,"type":"array"}},"required":["inputs","targets"],"type":"object"},"TerminalCommandPreviewTarget":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"command":{"type":"string"},"sessionId":{"type":"string"},"targetId":{"type":"string"},"title":{"type":"string"}},"required":["targetId","sessionId","alias","title","command"],"type":"object"},"TerminalCommandResult":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"problem":{"type":"string"},"sessionId":{"type":"string"},"status":{"enum":["delivered","failed"],"type":"string"},"targetId":{"type":"string"},"title":{"type":"string"}},"required":["targetId","sessionId","alias","title","status"],"type":"object"},"TerminalCommandTargetRequest":{"additionalProperties":false,"properties":{"sessionId":{"maxLength":64,"minLength":1,"type":"string"},"targetId":{"maxLength":255,"minLength":1,"type":"string"}},"required":["targetId","sessionId"],"type":"object"},"TerminalConnectionProgress":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hop":{"minimum":1,"type":"integer"},"hops":{"minimum":1,"type":"integer"},"hostName":{"type":"string"},"phase":{"enum":["dialing","host_key","authenticating","authenticated","opening_session"],"type":"string"},"user":{"type":"string"}},"required":["phase","alias","hostName","user","hop","hops"],"type":"object","x-sshc-less-than-or-equal":[["hop","hops"]]},"TerminalControlCursor":{"additionalProperties":false,"properties":{"end":{"description":"Monotonic end of the raw transcript; greater than next means unread bytes remain.","format":"uint64","minimum":0,"type":"integer"},"next":{"format":"uint64","minimum":0,"type":"integer"},"requested":{"format":"uint64","minimum":0,"type":"integer"},"start":{"format":"uint64","minimum":0,"type":"integer"},"truncated":{"type":"boolean"}},"required":["requested","start","next","end","truncated"],"type":"object"},"TerminalControlResponse":{"additionalProperties":false,"properties":{"cursor":{"$ref":"#/components/schemas/TerminalControlCursor"},"generation":{"format":"uint64","minimum":1,"type":"integer"},"output":{"description":"Bounded transcript with terminal escape and control sequences removed.","type":"string"},"session":{"$ref":"#/components/schemas/TerminalSession"},"state":{"enum":["connecting","connected","reconnecting","exited"],"type":"string"}},"required":["session","generation","state","cursor","output"],"type":"object"},"TerminalDestination":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"hostName":{"type":"string"},"port":{"type":"string"},"user":{"type":"string"}},"required":["alias","hostName","port","user"],"type":"object"},"TerminalDeterminateProgress":{"additionalProperties":false,"properties":{"percent":{"maximum":100,"minimum":0,"type":"integer"},"state":{"enum":["normal","error","paused"],"type":"string"}},"required":["state","percent"],"type":"object"},"TerminalDirectory":{"additionalProperties":false,"properties":{"path":{"maxLength":4096,"minLength":1,"type":"string"}},"required":["path"],"type":"object"},"TerminalExit":{"additionalProperties":false,"properties":{"at":{"type":"string"},"code":{"type":"integer"},"connectionLost":{"type":"boolean"},"signal":{"type":"string"}},"required":["code","signal","at"],"type":"object"},"TerminalFinishedCommand":{"additionalProperties":false,"properties":{"exitCode":{"type":"integer"},"seconds":{"minimum":0,"type":"integer"}},"required":["seconds"],"type":"object"},"TerminalForward":{"additionalProperties":false,"properties":{"id":{"maxLength":64,"minLength":1,"type":"string"},"kind":{"pattern":"^(local|remote|dynamic|agent)$","type":"string"},"listen":{"type":"string"},"problem":{"type":"string"},"temporary":{"type":"boolean"},"to":{"type":"string"}},"required":["id","kind","listen","to","problem","temporary"],"type":"object"},"TerminalHostKeyAnswer":{"additionalProperties":false,"properties":{"accept":{"type":"boolean"},"fingerprint":{"maxLength":128,"minLength":1,"type":"string"}},"required":["fingerprint","accept"],"type":"object"},"TerminalHostKeyQuestion":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"fingerprint":{"type":"string"},"hostName":{"type":"string"},"keyType":{"type":"string"}},"required":["alias","hostName","keyType","fingerprint"],"type":"object"},"TerminalIndeterminateProgress":{"additionalProperties":false,"properties":{"state":{"enum":["indeterminate"],"type":"string"}},"required":["state"],"type":"object"},"TerminalNotification":{"additionalProperties":false,"properties":{"body":{"type":"string"},"finishedCommand":{"$ref":"#/components/schemas/TerminalFinishedCommand"},"occurredAt":{"format":"date-time","type":"string"},"title":{"type":"string"}},"required":["title","body","occurredAt"],"type":"object"},"TerminalPasswordSaveAnswer":{"additionalProperties":false,"properties":{"save":{"type":"boolean"}},"required":["save"],"type":"object"},"TerminalPresentation":{"additionalProperties":false,"properties":{"displayTitle":{"type":"string"},"titlePinned":{"type":"boolean"},"titleSource":{"enum":["user","terminal","connection","fallback"],"type":"string"}},"required":["displayTitle","titleSource","titlePinned"],"type":"object"},"TerminalProgramProgress":{"oneOf":[{"$ref":"#/components/schemas/TerminalDeterminateProgress"},{"$ref":"#/components/schemas/TerminalIndeterminateProgress"}],"x-go-type":"json.RawMessage"},"TerminalProgramStatus":{"additionalProperties":false,"properties":{"app":{"maxLength":32,"type":"string"},"kind":{"enum":["permission","question","auth"],"type":"string"},"message":{"type":"string"},"progress":{"maximum":100,"minimum":0,"type":"integer"},"state":{"enum":["idle","working","blocked","done","error"],"type":"string"},"title":{"type":"string"},"version":{"minimum":1,"type":"integer"}},"required":["state","version"],"type":"object"},"TerminalReconnect":{"additionalProperties":false,"properties":{"attempt":{"maximum":5,"minimum":1,"type":"integer"},"limit":{"maximum":5,"minimum":1,"type":"integer"},"problem":{"type":"string"},"retryAt":{"format":"date-time","type":"string"},"waitingForNetwork":{"type":"boolean"}},"required":["attempt","limit","retryAt","problem"],"type":"object","x-sshc-less-than-or-equal":[["attempt","limit"]]},"TerminalRestoreOffer":{"additionalProperties":false,"properties":{"sessions":{"items":{"$ref":"#/components/schemas/TerminalRestoreSession"},"maxItems":256,"type":"array"}},"required":["sessions"],"type":"object"},"TerminalRestoreResult":{"additionalProperties":false,"properties":{"failed":{"minimum":0,"type":"integer"},"opened":{"minimum":0,"type":"integer"}},"required":["opened","failed"],"type":"object"},"TerminalRestoreSession":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"kind":{"enum":["ssh","shell"],"type":"string","x-enum-varnames":["TerminalRestoreSessionKindSsh","TerminalRestoreSessionKindShell"]},"pinnedTitle":{"type":"string"},"workingDirectory":{"type":"string"},"workspaceId":{"type":"string"}},"required":["kind","workspaceId"],"type":"object"},"TerminalResumeHint":{"additionalProperties":false,"properties":{"command":{"maxLength":8192,"type":"string"},"program":{"maxLength":64,"type":"string"}},"required":["program","command"],"type":"object"},"TerminalSession":{"additionalProperties":false,"properties":{"alias":{"type":"string"},"awaitingPrompt":{"type":"boolean"},"exited":{"$ref":"#/components/schemas/TerminalExit"},"forwards":{"items":{"$ref":"#/components/schemas/TerminalForward"},"type":"array"},"hostKeyQuestion":{"$ref":"#/components/schemas/TerminalHostKeyQuestion"},"id":{"type":"string"},"kind":{"enum":["ssh","shell"],"type":"string"},"lastNotification":{"$ref":"#/components/schemas/TerminalNotification"},"notificationVersion":{"minimum":0,"type":"integer"},"passwordSaveQuestion":{"type":"boolean"},"presentation":{"$ref":"#/components/schemas/TerminalPresentation"},"problem":{"type":"string"},"programProgress":{"$ref":"#/components/schemas/TerminalProgramProgress"},"programStatus":{"$ref":"#/components/schemas/TerminalProgramStatus"},"progress":{"$ref":"#/components/schemas/TerminalConnectionProgress"},"reconnect":{"$ref":"#/components/schemas/TerminalReconnect"},"resume":{"$ref":"#/components/schemas/TerminalResumeHint"},"shellProfileId":{"type":"string"},"startedAt":{"type":"string"},"state":{"enum":["connecting","connected","reconnecting","exited"],"type":"string"},"title":{"type":"string"},"workingDirectory":{"type":"string"},"workspaceId":{"type":"string"}},"required":["id","kind","title","workspaceId","startedAt","state","problem"],"type":"object"},"TerminalSessionList":{"additionalProperties":false,"properties":{"maxSessions":{"maximum":200,"minimum":0,"type":"integer"},"restoreOffer":{"maxLength":64,"minLength":1,"type":"string"},"sessions":{"items":{"$ref":"#/components/schemas/TerminalSession"},"type":"array"}},"required":["sessions","maxSessions"],"type":"object"},"TerminalStreamTicket":{"additionalProperties":false,"properties":{"streamTicket":{"type":"string"}},"required":["streamTicket"],"type":"object"},"TerminalWorkspace":{"additionalProperties":false,"properties":{"builtIn":{"type":"boolean"},"hidden":{"type":"boolean"},"id":{"type":"string"},"name":{"maxLength":64,"type":"string"}},"required":["id","name","hidden","builtIn"],"type":"object"},"TerminalWorkspaceList":{"additionalProperties":false,"properties":{"maxWorkspaces":{"minimum":1,"type":"integer"},"workspaces":{"items":{"$ref":"#/components/schemas/TerminalWorkspace"},"type":"array"}},"required":["workspaces","maxWorkspaces"],"type":"object"},"TrashEntrySummary":{"additionalProperties":false,"properties":{"ageDays":{"type":"integer"},"blockers":{"items":{"type":"string"},"type":"array"},"files":{"items":{"$ref":"#/components/schemas/TrashFileSummary"},"type":"array"},"id":{"type":"string"},"restorable":{"type":"boolean"},"stale":{"type":"boolean"}},"required":["id","ageDays","stale","files","restorable","blockers"],"type":"object"},"TrashFileSummary":{"additionalProperties":false,"properties":{"fingerprint":{"type":"string"},"kind":{"type":"string"},"originalRelativePath":{"type":"string"},"permission":{"type":"string"}},"required":["originalRelativePath","kind","fingerprint","permission"],"type":"object"},"TrashKeyResponse":{"additionalProperties":false,"properties":{"entryId":{"type":"string"},"files":{"items":{"$ref":"#/components/schemas/TrashFileSummary"},"type":"array"},"skipped":{"items":{"type":"string"},"type":"array"},"transactionId":{"type":"string"}},"required":["entryId","files","skipped","transactionId"],"type":"object"},"TrashListResponse":{"additionalProperties":false,"properties":{"entries":{"items":{"$ref":"#/components/schemas/TrashEntrySummary"},"type":"array"},"retentionDays":{"type":"integer"}},"required":["entries","retentionDays"],"type":"object"},"UnreadableFile":{"additionalProperties":false,"properties":{"reason":{"type":"string"},"relativePath":{"type":"string"}},"required":["relativePath","reason"],"type":"object"},"UnresolvedReference":{"additionalProperties":false,"properties":{"configPath":{"type":"string"},"directive":{"type":"string"},"line":{"type":"integer"},"reason":{"type":"string"},"value":{"type":"string"}},"required":["directive","value","configPath","line","reason"],"type":"object"},"UpdateConnectionKeyPassphrase":{"discriminator":{"mapping":{"set_dedicated":"#/components/schemas/ConnectionKeyPassphraseSetDedicated","unchanged":"#/components/schemas/ConnectionKeyPassphraseUnchanged"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/ConnectionKeyPassphraseUnchanged"},{"$ref":"#/components/schemas/ConnectionKeyPassphraseSetDedicated"}],"x-go-type":"json.RawMessage"},"UpdateConnectionPassword":{"discriminator":{"mapping":{"confirm_route":"#/components/schemas/UpdatePasswordConfirmRoute","dedicated_password":"#/components/schemas/CreateDedicatedPasswordAuthentication","new_shared_password":"#/components/schemas/CreateNewSharedPasswordAuthentication","remove":"#/components/schemas/UpdatePasswordRemove","saved_password":"#/components/schemas/CreateSavedPasswordAuthentication","unchanged":"#/components/schemas/UpdatePasswordUnchanged"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/UpdatePasswordUnchanged"},{"$ref":"#/components/schemas/CreateDedicatedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateSavedPasswordAuthentication"},{"$ref":"#/components/schemas/CreateNewSharedPasswordAuthentication"},{"$ref":"#/components/schemas/UpdatePasswordConfirmRoute"},{"$ref":"#/components/schemas/UpdatePasswordRemove"}],"x-go-type":"json.RawMessage"},"UpdateConnectionRequest":{"additionalProperties":false,"properties":{"base":{"maxLength":1048576,"type":"string"},"hostName":{"$ref":"#/components/schemas/ConnectionStringChange"},"identity":{"$ref":"#/components/schemas/HostIdentity"},"identityFile":{"$ref":"#/components/schemas/ConnectionIdentityFileChange"},"keyPassphrase":{"$ref":"#/components/schemas/UpdateConnectionKeyPassphrase"},"password":{"$ref":"#/components/schemas/UpdateConnectionPassword"},"port":{"$ref":"#/components/schemas/ConnectionPortChange"},"totp":{"$ref":"#/components/schemas/UpdateConnectionTOTP"},"user":{"$ref":"#/components/schemas/ConnectionStringChange"}},"required":["identity","base","password","keyPassphrase","totp"],"type":"object"},"UpdateConnectionTOTP":{"discriminator":{"mapping":{"confirm_route":"#/components/schemas/UpdateTOTPConfirmRoute","remove":"#/components/schemas/UpdateTOTPRemove","saved_totp":"#/components/schemas/UpdateTOTPSaved","unchanged":"#/components/schemas/UpdateTOTPUnchanged"},"propertyName":"kind"},"oneOf":[{"$ref":"#/components/schemas/UpdateTOTPUnchanged"},{"$ref":"#/components/schemas/UpdateTOTPSaved"},{"$ref":"#/components/schemas/UpdateTOTPConfirmRoute"},{"$ref":"#/components/schemas/UpdateTOTPRemove"}],"x-go-type":"json.RawMessage"},"UpdateCredentialRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":128,"minLength":1,"type":"string"},"secret":{"maxLength":1024,"minLength":1,"type":"string"}},"required":["name","secret"],"type":"object"},"UpdateJob":{"additionalProperties":false,"properties":{"id":{"type":"string"},"installedVersion":{"description":"Stable version reported by the installed executable.","type":"string"},"problem":{"type":"string"},"state":{"enum":["accepted","installing","restarting","succeeded","failed","restart_required"],"type":"string"},"target":{"type":"string"}},"required":["id","target","state","problem"],"type":"object"},"UpdateLog":{"additionalProperties":false,"properties":{"cursor":{"$ref":"#/components/schemas/TerminalControlCursor"},"jobId":{"type":"string"},"output":{"maxLength":65536,"type":"string"},"reachedCapacity":{"type":"boolean"}},"required":["jobId","cursor","reachedCapacity","output"],"type":"object"},"UpdatePasswordConfirmRoute":{"additionalProperties":false,"properties":{"kind":{"enum":["confirm_route"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdatePasswordRemove":{"additionalProperties":false,"properties":{"kind":{"enum":["remove"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdatePasswordUnchanged":{"additionalProperties":false,"properties":{"kind":{"enum":["unchanged"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdatePreview":{"additionalProperties":false,"properties":{"actionToken":{"type":"string"},"current":{"type":"string"},"manager":{"enum":["homebrew","install.sh","install.ps1"],"type":"string"},"target":{"type":"string"}},"required":["current","target","manager","actionToken"],"type":"object"},"UpdateRequest":{"additionalProperties":false,"properties":{"target":{"maxLength":128,"minLength":1,"type":"string"}},"required":["target"],"type":"object"},"UpdateStatus":{"additionalProperties":false,"properties":{"available":{"type":"boolean"},"canUpdate":{"type":"boolean"},"checkError":{"description":"Failure of the last check; previous successful release information is retained.","type":"string"},"checkedAt":{"description":"Completion time of the last check","format":"date-time","successful or failed.":null,"type":"string"},"current":{"type":"string"},"job":{"$ref":"#/components/schemas/UpdateJob"},"latest":{"type":"string"},"manager":{"enum":["homebrew","install.sh","install.ps1"],"type":"string"},"pageUrl":{"type":"string"},"publishedAt":{"format":"date-time","type":"string"},"reason":{"description":"Stable reason code explaining why automatic updates are unavailable","type":"string"},"releases":{"items":{"$ref":"#/components/schemas/ReleaseNotes"},"maxItems":100,"type":"array"}},"required":["current","available"],"type":"object"},"UpdateTOTPConfirmRoute":{"additionalProperties":false,"properties":{"kind":{"enum":["confirm_route"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdateTOTPRemove":{"additionalProperties":false,"properties":{"kind":{"enum":["remove"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdateTOTPSaved":{"additionalProperties":false,"properties":{"credential":{"maxLength":128,"minLength":1,"type":"string"},"kind":{"enum":["saved_totp"],"type":"string","x-go-type":"string"}},"required":["kind","credential"],"type":"object"},"UpdateTOTPUnchanged":{"additionalProperties":false,"properties":{"kind":{"enum":["unchanged"],"type":"string","x-go-type":"string"}},"required":["kind"],"type":"object"},"UpdateTerminalWorkspaceRequest":{"additionalProperties":false,"properties":{"hidden":{"type":"boolean"},"name":{"maxLength":64,"type":"string"}},"type":"object"},"VPNBindingRequest":{"additionalProperties":false,"properties":{"alias":{"maxLength":64,"minLength":1,"type":"string"},"profile":{"maxLength":48,"type":"string"}},"required":["alias","profile"],"type":"object"},"VPNLogs":{"additionalProperties":false,"properties":{"lines":{"maxLength":65536,"type":"string"}},"required":["lines"],"type":"object"},"VPNOverview":{"additionalProperties":false,"properties":{"available":{"type":"boolean"},"checking":{"type":"boolean"},"detail":{"type":"string"},"profiles":{"items":{"$ref":"#/components/schemas/VPNProfileStatus"},"maxItems":64,"type":"array"},"unavailable":{"enum":["vpn_docker_missing","vpn_docker_not_running"],"type":"string"}},"required":["available","profiles","checking"],"type":"object"},"VPNProfile":{"additionalProperties":false,"properties":{"backend":{"enum":["wireguard","l2tp_ipsec","openconnect","openvpn","ikev2"],"type":"string"},"dns":{"items":{"maxLength":15,"minLength":7,"type":"string"},"maxItems":3,"type":"array"},"id":{"pattern":"^[0-9a-f]{32}$","type":"string"},"ikev2":{"$ref":"#/components/schemas/IKEv2Profile"},"l2tp":{"$ref":"#/components/schemas/L2TPProfile"},"name":{"maxLength":48,"minLength":1,"type":"string"},"openconnect":{"$ref":"#/components/schemas/OpenConnectProfile"},"openvpn":{"$ref":"#/components/schemas/OpenVPNProfile"},"wireguard":{"$ref":"#/components/schemas/WireGuardProfile"}},"required":["name","backend"],"type":"object"},"VPNProfileRequest":{"additionalProperties":false,"properties":{"profile":{"$ref":"#/components/schemas/VPNProfile"},"secrets":{"$ref":"#/components/schemas/VPNSecrets"}},"required":["profile"],"type":"object"},"VPNProfileStatus":{"additionalProperties":false,"properties":{"connections":{"items":{"type":"string"},"type":"array"},"openConnections":{"minimum":0,"type":"integer"},"phase":{"enum":["image","container","tunnel","approval"],"type":"string"},"profile":{"$ref":"#/components/schemas/VPNProfile"},"relaySocket":{"type":"string"},"running":{"type":"boolean"},"tunnel":{"$ref":"#/components/schemas/VPNTunnel"}},"required":["profile","running","relaySocket","connections","openConnections"],"type":"object"},"VPNRenameRequest":{"additionalProperties":false,"properties":{"name":{"maxLength":48,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"VPNSecrets":{"additionalProperties":false,"properties":{"ikev2Password":{"maxLength":256,"minLength":1,"type":"string"},"ikev2Psk":{"maxLength":256,"minLength":1,"type":"string"},"ipsecPsk":{"maxLength":256,"minLength":1,"type":"string"},"l2tpPassword":{"maxLength":256,"minLength":1,"type":"string"},"openconnectPassword":{"maxLength":256,"minLength":1,"type":"string"},"openconnectTotpSecret":{"maxLength":512,"minLength":1,"type":"string"},"openvpnConfig":{"maxLength":65536,"minLength":1,"type":"string"},"openvpnPassword":{"maxLength":256,"minLength":1,"type":"string"},"wireguardConfig":{"maxLength":16384,"minLength":1,"type":"string"}},"type":"object"},"VPNTunnel":{"additionalProperties":false,"properties":{"address":{"type":"string"},"backend":{"type":"string"},"interface":{"type":"string"},"since":{"type":"string"}},"type":"object"},"ValueSource":{"additionalProperties":false,"properties":{"condition":{"type":"string"},"keyword":{"type":"string"},"kind":{"type":"string"},"line":{"type":"integer"},"path":{"type":"string"},"value":{"type":"string"},"winner":{"type":"boolean"}},"required":["keyword","value","path","line","condition","kind","winner"],"type":"object"},"VaultAutoLockSettings":{"additionalProperties":false,"properties":{"mode":{"description":"idleは無操作時間後、restartは手動ロックまたはengine終了時だけロックする。","enum":["idle","restart"],"type":"string"},"unit":{"enum":["minutes","hours"],"type":"string"},"value":{"maximum":999,"minimum":1,"type":"integer"}},"required":["mode"],"type":"object"},"WireGuardProfile":{"additionalProperties":false,"properties":{"servers":{"items":{"maxLength":320,"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"}},"required":["servers"],"type":"object"}} as const satisfies Readonly>; +const endpoints = [{"method":"GET","path":"/api/v1/about","responses":{"200":{"$ref":"#/components/schemas/About"}}},{"method":"POST","path":"/api/v1/actions","requestRequired":true,"request":{"$ref":"#/components/schemas/IssueActionRequest"},"responses":{"201":{"$ref":"#/components/schemas/IssueActionResponse"}}},{"method":"GET","path":"/api/v1/config/file","responses":{"200":{"$ref":"#/components/schemas/FileContents"}}},{"method":"POST","path":"/api/v1/config/groups/delete","requestRequired":true,"request":{"$ref":"#/components/schemas/GroupDeleteRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"POST","path":"/api/v1/config/groups/rename","requestRequired":true,"request":{"$ref":"#/components/schemas/GroupRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"GET","path":"/api/v1/config/host","responses":{"200":{"$ref":"#/components/schemas/HostDetail"}}},{"method":"GET","path":"/api/v1/config/overview","responses":{"200":{"$ref":"#/components/schemas/Overview"}}},{"method":"POST","path":"/api/v1/config/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/EditRequest"},"responses":{"200":{"$ref":"#/components/schemas/SavePreview"}}},{"method":"POST","path":"/api/v1/config/save","requestRequired":true,"request":{"$ref":"#/components/schemas/EditRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"PATCH","path":"/api/v1/connections","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateConnectionRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"POST","path":"/api/v1/connections","requestRequired":true,"request":{"$ref":"#/components/schemas/CreateConnectionRequest"},"responses":{"201":{"$ref":"#/components/schemas/CreateConnectionResponse"}}},{"method":"GET","path":"/api/v1/connections/recent","responses":{"200":{"$ref":"#/components/schemas/RecentConnectionList"}}},{"method":"GET","path":"/api/v1/credentials","responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"POST","path":"/api/v1/credentials/totp/{name}/codes","responses":{"200":{"$ref":"#/components/schemas/TOTPCodeSet"}}},{"method":"PUT","path":"/api/v1/credentials/{kind}/assign","requestRequired":true,"request":{"$ref":"#/components/schemas/AssignCredentialRequest"},"responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"DELETE","path":"/api/v1/credentials/{kind}/assign/{subject}","responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"DELETE","path":"/api/v1/credentials/{kind}/{name}","responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"PATCH","path":"/api/v1/credentials/{kind}/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateCredentialRequest"},"responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"PUT","path":"/api/v1/credentials/{kind}/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/StoreCredentialRequest"},"responses":{"200":{"$ref":"#/components/schemas/CredentialList"}}},{"method":"POST","path":"/api/v1/credentials/{kind}/{name}/reveal","responses":{"200":{"$ref":"#/components/schemas/RevealCredentialResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/authentication","requestRequired":true,"request":{"$ref":"#/components/schemas/AuthenticationRequest"},"responses":{"200":{"$ref":"#/components/schemas/AuthenticationResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/config","responses":{"200":{"$ref":"#/components/schemas/ConfigCheckResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/effective","requestRequired":true,"request":{"$ref":"#/components/schemas/AliasRequest"},"responses":{"200":{"$ref":"#/components/schemas/EffectiveResponse"}}},{"method":"POST","path":"/api/v1/diagnostics/reachability","requestRequired":true,"request":{"$ref":"#/components/schemas/AliasRequest"},"responses":{"200":{"$ref":"#/components/schemas/ReachabilityResponse"}}},{"method":"GET","path":"/api/v1/engines","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineList"}}},{"method":"POST","path":"/api/v1/engines","requestRequired":true,"request":{"$ref":"#/components/schemas/AddRemoteEngineRequest"},"responses":{"201":{"$ref":"#/components/schemas/RemoteEngine"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/control","requestRequired":true,"request":{"$ref":"#/components/schemas/RemoteEngineActionRequest"},"responses":{"202":{"$ref":"#/components/schemas/RemoteEngineActionStarted"}}},{"method":"GET","path":"/api/v1/engines/{alias}/control/log","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineActionLog"}}},{"method":"POST","path":"/api/v1/engines/{alias}/open","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineEntrance"}}},{"method":"GET","path":"/api/v1/engines/{alias}/status","responses":{"200":{"$ref":"#/components/schemas/RemoteEngineStatus"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/commands","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandDispatchRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandDispatchResponse"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/commands/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandPreviewRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandPreview"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/connection-history","responses":{"200":{"$ref":"#/components/schemas/ConnectionHistory"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/destinations/{connectionAlias}","responses":{"200":{"$ref":"#/components/schemas/TerminalDestination"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/machine","responses":{"200":{"$ref":"#/components/schemas/MachineStatus"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/restore","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreOffer"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreResult"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/sessions","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions","requestRequired":true,"request":{"$ref":"#/components/schemas/OpenTerminalSessionRequest"},"responses":{"201":{"$ref":"#/components/schemas/OpenTerminalSessionResponse"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/sessions/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/directory","responses":{"200":{"$ref":"#/components/schemas/TerminalDirectory"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/forwards","requestRequired":true,"request":{"$ref":"#/components/schemas/StartTerminalForwardRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/forwards/{forwardId}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/host-key","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalHostKeyAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/password-save","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalPasswordSaveAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/reconnect","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/reconnect/stop","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/stream","responses":{"201":{"$ref":"#/components/schemas/TerminalStreamTicket"}}},{"method":"PUT","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/title","requestRequired":true,"request":{"$ref":"#/components/schemas/SetTerminalSessionTitleRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"PUT","path":"/api/v1/engines/{alias}/terminal/sessions/{id}/workspace","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalSessionRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/shell-profiles","responses":{"200":{"$ref":"#/components/schemas/LocalShellProfileList"}}},{"method":"GET","path":"/api/v1/engines/{alias}/terminal/workspaces","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/terminal/workspaces","requestRequired":true,"request":{"$ref":"#/components/schemas/CreateTerminalWorkspaceRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"DELETE","path":"/api/v1/engines/{alias}/terminal/workspaces/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"PATCH","path":"/api/v1/engines/{alias}/terminal/workspaces/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"PUT","path":"/api/v1/engines/{alias}/terminal/workspaces/{id}/position","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"POST","path":"/api/v1/engines/{alias}/vault/unlock","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/health","responses":{"200":{"$ref":"#/components/schemas/HealthResponse"}}},{"method":"GET","path":"/api/v1/history","responses":{"200":{"$ref":"#/components/schemas/HistoryList"}}},{"method":"GET","path":"/api/v1/history/pending/{id}","responses":{"200":{"$ref":"#/components/schemas/PendingChanges"}}},{"method":"POST","path":"/api/v1/history/recover","requestRequired":true,"request":{"$ref":"#/components/schemas/RecoverRequest"},"responses":{"200":{"$ref":"#/components/schemas/RecoverResponse"}}},{"method":"POST","path":"/api/v1/history/restore","requestRequired":true,"request":{"$ref":"#/components/schemas/RestoreRequest"},"responses":{"200":{"$ref":"#/components/schemas/SaveResult"}}},{"method":"GET","path":"/api/v1/keys","responses":{"200":{"$ref":"#/components/schemas/KeyInventoryResponse"}}},{"method":"POST","path":"/api/v1/keys","requestRequired":true,"request":{"$ref":"#/components/schemas/GenerateKeyRequest"},"responses":{"201":{"$ref":"#/components/schemas/GenerateKeyResponse"}}},{"method":"GET","path":"/api/v1/keys/algorithms","responses":{"200":{"$ref":"#/components/schemas/KeyAlgorithmsResponse"}}},{"method":"POST","path":"/api/v1/keys/hardware-command","requestRequired":true,"request":{"$ref":"#/components/schemas/HardwareCommandRequest"},"responses":{"200":{"$ref":"#/components/schemas/HardwareCommandResponse"}}},{"method":"DELETE","path":"/api/v1/keys/{keyId}/agent","responses":{"200":{"$ref":"#/components/schemas/AgentIdentitiesResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/agent","requestRequired":true,"request":{"$ref":"#/components/schemas/RegisterKeyRequest"},"responses":{"200":{"$ref":"#/components/schemas/RegisterKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/location","requestRequired":true,"request":{"$ref":"#/components/schemas/RelocateKeyRequest"},"responses":{"200":{"$ref":"#/components/schemas/RelocateKeyResponse"},"409":{"$ref":"#/components/schemas/RelocateKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/passphrase","requestRequired":true,"request":{"$ref":"#/components/schemas/ChangePassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/ChangePassphraseResponse"}}},{"method":"GET","path":"/api/v1/keys/{keyId}/public","responses":{"200":{"$ref":"#/components/schemas/PublicKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/reveal","responses":{"200":{"$ref":"#/components/schemas/RevealPrivateKeyResponse"}}},{"method":"POST","path":"/api/v1/keys/{keyId}/trash","responses":{"200":{"$ref":"#/components/schemas/TrashKeyResponse"}}},{"method":"GET","path":"/api/v1/known-hosts","responses":{"200":{"$ref":"#/components/schemas/KnownHostsResponse"}}},{"method":"POST","path":"/api/v1/known-hosts/add","requestRequired":true,"request":{"$ref":"#/components/schemas/KnownHostsAddRequest"},"responses":{"200":{"$ref":"#/components/schemas/KnownHostsChangeResponse"}}},{"method":"POST","path":"/api/v1/known-hosts/delete","requestRequired":true,"request":{"$ref":"#/components/schemas/KnownHostsDeleteRequest"},"responses":{"200":{"$ref":"#/components/schemas/KnownHostsChangeResponse"}}},{"method":"POST","path":"/api/v1/known-hosts/scan","requestRequired":true,"request":{"$ref":"#/components/schemas/KnownHostsScanRequest"},"responses":{"200":{"$ref":"#/components/schemas/KnownHostsScanResponse"}}},{"method":"GET","path":"/api/v1/metadata","responses":{"200":{"$ref":"#/components/schemas/Metadata"}}},{"method":"PUT","path":"/api/v1/metadata/keybar","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"items":{"$ref":"#/components/schemas/KeyBarPreset"},"maxItems":64,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/KeyBarPreset"},"maxItems":64,"type":"array"}},"required":["base","presets"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/metadata/shortcuts","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"items":{"$ref":"#/components/schemas/ShortcutPreset"},"maxItems":64,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/ShortcutPreset"},"maxItems":64,"type":"array"}},"required":["base","presets"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"GET","path":"/api/v1/passwords","responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/change","requestRequired":true,"request":{"$ref":"#/components/schemas/ChangeMasterPasswordRequest"},"responses":{"200":{"$ref":"#/components/schemas/ChangeMasterPasswordResult"}}},{"method":"POST","path":"/api/v1/passwords/initialise","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/lock","responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/recover-compatible-backup","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/reset-unsupported","requestRequired":true,"request":{"$ref":"#/components/schemas/ResetUnsupportedVaultRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"POST","path":"/api/v1/passwords/unlock","requestRequired":true,"request":{"$ref":"#/components/schemas/PassphraseRequest"},"responses":{"200":{"$ref":"#/components/schemas/PasswordVaultStatus"}}},{"method":"GET","path":"/api/v1/passwords/{alias}/eligibility","responses":{"200":{"$ref":"#/components/schemas/PasswordEligibility"}}},{"method":"POST","path":"/api/v1/remote-keys/plan","requestRequired":true,"request":{"$ref":"#/components/schemas/RemoteKeyPlanRequest"},"responses":{"200":{"$ref":"#/components/schemas/RemoteKeyPlan"}}},{"method":"POST","path":"/api/v1/remote-keys/register","requestRequired":true,"request":{"$ref":"#/components/schemas/RemoteKeyRegisterRequest"},"responses":{"200":{"$ref":"#/components/schemas/RemoteKeyRegisterResponse"}}},{"method":"POST","path":"/api/v1/session/bootstrap","responses":{"200":{"$ref":"#/components/schemas/BootstrapResponse"}}},{"method":"POST","path":"/api/v1/session/recover","responses":{"200":{"$ref":"#/components/schemas/BootstrapResponse"}}},{"method":"POST","path":"/api/v1/session/renew","responses":{"200":{"$ref":"#/components/schemas/BootstrapResponse"}}},{"method":"POST","path":"/api/v1/session/sign-out","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/settings","responses":{"200":{"$ref":"#/components/schemas/SettingsDocument"}}},{"method":"PUT","path":"/api/v1/settings/global","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"$ref":"#/components/schemas/GlobalSettings"},"settings":{"$ref":"#/components/schemas/GlobalSettings"}},"required":["base","settings"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"DELETE","path":"/api/v1/settings/machines/{id}","responses":{},"noContent":["204"]},{"method":"PUT","path":"/api/v1/settings/machines/{id}","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"$ref":"#/components/schemas/MachineSettings"},"settings":{"$ref":"#/components/schemas/MachineSettings"}},"required":["base","settings"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/settings/presets","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"},"presets":{"items":{"$ref":"#/components/schemas/SettingsPreset"},"maxItems":16,"minItems":1,"type":"array"}},"required":["base","presets"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/settings/presets/{id}","requestRequired":true,"request":{"additionalProperties":false,"properties":{"base":{"$ref":"#/components/schemas/SettingsPreset"},"preset":{"$ref":"#/components/schemas/SettingsPreset"}},"required":["base","preset"],"type":"object"},"responses":{"200":{"$ref":"#/components/schemas/SettingsSaveResult"}}},{"method":"PUT","path":"/api/v1/settings/state","requestRequired":true,"request":{"additionalProperties":false,"minProperties":1,"properties":{"interface":{"$ref":"#/components/schemas/InterfaceState"},"transfersStopped":{"type":"boolean"}},"type":"object"},"responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/sftp/compare","responses":{"200":{"$ref":"#/components/schemas/SFTPDirectoryComparison"}}},{"method":"POST","path":"/api/v1/sftp/local/delete","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalDeleteRequest"},"responses":{},"noContent":["204"]},{"method":"POST","path":"/api/v1/sftp/local/delete-plan","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalDeleteSelection"},"responses":{"200":{"$ref":"#/components/schemas/SFTPLocalDeletePlan"}}},{"method":"POST","path":"/api/v1/sftp/local/directories","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalMkdirRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/local/download","responses":{},"noContent":["416"]},{"method":"GET","path":"/api/v1/sftp/local/entries","responses":{"200":{"$ref":"#/components/schemas/SFTPLocalListing"}}},{"method":"GET","path":"/api/v1/sftp/local/preview","responses":{}},{"method":"POST","path":"/api/v1/sftp/local/rename","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPLocalRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/local/text","responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"PUT","path":"/api/v1/sftp/local/text","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPSaveTextRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"GET","path":"/api/v1/sftp/local/thumbnail","responses":{}},{"method":"POST","path":"/api/v1/sftp/media-tickets","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPMediaTicketRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPMediaTicket"}}},{"method":"DELETE","path":"/api/v1/sftp/media-tickets/{ticket}","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/sftp/media/{ticket}","responses":{},"noContent":["416"]},{"method":"GET","path":"/api/v1/sftp/transfers","responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJobList"}}},{"method":"POST","path":"/api/v1/sftp/transfers","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCreateTransferJobRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPTransferJob"}}},{"method":"DELETE","path":"/api/v1/sftp/transfers/finished","responses":{},"noContent":["204"]},{"method":"PUT","path":"/api/v1/sftp/transfers/settings","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPTransferSettingsRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJobList"}}},{"method":"DELETE","path":"/api/v1/sftp/transfers/{id}","responses":{},"noContent":["204"]},{"method":"POST","path":"/api/v1/sftp/transfers/{id}/actions","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPTransferJobActionRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJob"}}},{"method":"POST","path":"/api/v1/sftp/transfers/{id}/download-checkpoint","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPDownloadCheckpointRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJob"}}},{"method":"POST","path":"/api/v1/sftp/transfers/{id}/queue-position","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPTransferQueueMoveRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTransferJobList"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/archive","responses":{}},{"method":"GET","path":"/api/v1/sftp/{alias}/connection-attempts/{attempt}","responses":{"200":{"$ref":"#/components/schemas/SFTPConnectionAttempt"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/connection-attempts/{attempt}/host-key","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalHostKeyAnswer"},"responses":{"200":{"$ref":"#/components/schemas/SFTPConnectionAttempt"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/download","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/sftp/{alias}/entries","responses":{"200":{"$ref":"#/components/schemas/SFTPListing"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/entries","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCreateEntryRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/entry","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/mode","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChmodRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/mode-plan","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChmodSelection"},"responses":{"200":{"$ref":"#/components/schemas/SFTPChmodPlan"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/modes","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChmodSelectionRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPChmodResult"},"207":{"$ref":"#/components/schemas/SFTPChmodResult"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/ownership","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPOwnershipRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/preview","responses":{}},{"method":"GET","path":"/api/v1/sftp/{alias}/search","responses":{"200":{"$ref":"#/components/schemas/SFTPSearchResult"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/space","responses":{"200":{"$ref":"#/components/schemas/SFTPFilesystemSpace"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/stats","responses":{"200":{"$ref":"#/components/schemas/SFTPDirectoryStats"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/symlink","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPChangeSymlinkRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/symlink","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCreateSymlinkRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPEntry"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/text","responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"PUT","path":"/api/v1/sftp/{alias}/text","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPSaveTextRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPTextFile"}}},{"method":"GET","path":"/api/v1/sftp/{alias}/thumbnail","responses":{}},{"method":"DELETE","path":"/api/v1/sftp/{alias}/uploads/{id}","responses":{"200":{"$ref":"#/components/schemas/ChangedResponse"}}},{"method":"PATCH","path":"/api/v1/sftp/{alias}/uploads/{id}","requestRequired":true,"responses":{"200":{"$ref":"#/components/schemas/SFTPResumableUpload"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/uploads/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPStartUploadRequest"},"responses":{"200":{"$ref":"#/components/schemas/SFTPResumableUpload"}}},{"method":"POST","path":"/api/v1/sftp/{alias}/uploads/{id}/complete","requestRequired":true,"request":{"$ref":"#/components/schemas/SFTPCompleteUploadRequest"},"responses":{"201":{"$ref":"#/components/schemas/SFTPTransfer"}}},{"method":"GET","path":"/api/v1/snippets","responses":{"200":{"$ref":"#/components/schemas/SnippetLibrary"}}},{"method":"POST","path":"/api/v1/snippets","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetDraft"},"responses":{"201":{"$ref":"#/components/schemas/Snippet"}}},{"method":"POST","path":"/api/v1/snippets/jobs","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetExecuteRequest"},"responses":{"202":{"$ref":"#/components/schemas/SnippetJob"}}},{"method":"DELETE","path":"/api/v1/snippets/jobs/{id}","responses":{"202":{"$ref":"#/components/schemas/SnippetJob"}}},{"method":"GET","path":"/api/v1/snippets/jobs/{id}","responses":{"200":{"$ref":"#/components/schemas/SnippetJob"}}},{"method":"POST","path":"/api/v1/snippets/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetPreviewRequest"},"responses":{"200":{"$ref":"#/components/schemas/SnippetPreview"}}},{"method":"PUT","path":"/api/v1/snippets/startup/{alias}","requestRequired":true,"request":{"$ref":"#/components/schemas/StartupSnippetRequest"},"responses":{"200":{"$ref":"#/components/schemas/SnippetLibrary"}}},{"method":"DELETE","path":"/api/v1/snippets/{id}","responses":{"200":{"$ref":"#/components/schemas/ChangedResponse"}}},{"method":"PUT","path":"/api/v1/snippets/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/SnippetDraft"},"responses":{"200":{"$ref":"#/components/schemas/Snippet"}}},{"method":"GET","path":"/api/v1/sync","responses":{"200":{"$ref":"#/components/schemas/SyncStatus"}}},{"method":"PUT","path":"/api/v1/sync/auto","requestRequired":true,"request":{"$ref":"#/components/schemas/AutoSyncRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncStatus"}}},{"method":"GET","path":"/api/v1/sync/bucket","responses":{"200":{"$ref":"#/components/schemas/SyncBucketStatus"}}},{"method":"GET","path":"/api/v1/sync/exclusions","responses":{"200":{"$ref":"#/components/schemas/SyncExclusions"}}},{"method":"PUT","path":"/api/v1/sync/exclusions","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncExclusionsRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncExclusions"}}},{"method":"POST","path":"/api/v1/sync/force-push","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncPushRequest"},"responses":{"200":{"$ref":"#/components/schemas/PushResponse"}}},{"method":"GET","path":"/api/v1/sync/history","responses":{"200":{"$ref":"#/components/schemas/SyncHistory"}}},{"method":"POST","path":"/api/v1/sync/history/diff","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncHistoryDiffRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncHistoryDiff"}}},{"method":"PUT","path":"/api/v1/sync/key","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncKeyRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncKeyResponse"}}},{"method":"POST","path":"/api/v1/sync/now","responses":{"200":{"$ref":"#/components/schemas/SyncStatus"}}},{"method":"POST","path":"/api/v1/sync/pull","requestRequired":true,"request":{"$ref":"#/components/schemas/PullRequest"},"responses":{"200":{"$ref":"#/components/schemas/PullResponse"}}},{"method":"GET","path":"/api/v1/sync/push","responses":{"200":{"$ref":"#/components/schemas/SyncPushDraft"}}},{"method":"POST","path":"/api/v1/sync/push","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncPushRequest"},"responses":{"200":{"$ref":"#/components/schemas/PushResponse"}}},{"method":"PUT","path":"/api/v1/sync/setup","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncSetupRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncSetupResponse"}}},{"method":"POST","path":"/api/v1/sync/setup/check","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncSetupCheckRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncSetupCheckResponse"}}},{"method":"POST","path":"/api/v1/sync/setup/export","responses":{"200":{"$ref":"#/components/schemas/SyncSetupExport"}}},{"method":"POST","path":"/api/v1/sync/setup/import","requestRequired":true,"request":{"$ref":"#/components/schemas/SyncSetupImportRequest"},"responses":{"200":{"$ref":"#/components/schemas/SyncSetupResponse"}}},{"method":"GET","path":"/api/v1/terminal/backgrounds","responses":{"200":{"$ref":"#/components/schemas/TerminalBackgroundList"}}},{"method":"POST","path":"/api/v1/terminal/backgrounds","requestRequired":true,"responses":{"201":{"$ref":"#/components/schemas/TerminalBackground"}}},{"method":"PUT","path":"/api/v1/terminal/backgrounds/capacity","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalBackgroundCapacityRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalBackgroundList"}}},{"method":"DELETE","path":"/api/v1/terminal/backgrounds/{name}","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/terminal/backgrounds/{name}","responses":{}},{"method":"PATCH","path":"/api/v1/terminal/backgrounds/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/RenameTerminalBackgroundRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalBackground"}}},{"method":"POST","path":"/api/v1/terminal/commands","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandDispatchRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandDispatchResponse"}}},{"method":"POST","path":"/api/v1/terminal/commands/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalCommandPreviewRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalCommandPreview"}}},{"method":"GET","path":"/api/v1/terminal/connection-history","responses":{"200":{"$ref":"#/components/schemas/ConnectionHistory"}}},{"method":"GET","path":"/api/v1/terminal/destinations/{connectionAlias}","responses":{"200":{"$ref":"#/components/schemas/TerminalDestination"}}},{"method":"GET","path":"/api/v1/terminal/machine","responses":{"200":{"$ref":"#/components/schemas/MachineStatus"}}},{"method":"DELETE","path":"/api/v1/terminal/restore","responses":{},"noContent":["204"]},{"method":"GET","path":"/api/v1/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreOffer"}}},{"method":"POST","path":"/api/v1/terminal/restore","responses":{"200":{"$ref":"#/components/schemas/TerminalRestoreResult"}}},{"method":"GET","path":"/api/v1/terminal/sessions","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions","requestRequired":true,"request":{"$ref":"#/components/schemas/OpenTerminalSessionRequest"},"responses":{"201":{"$ref":"#/components/schemas/OpenTerminalSessionResponse"}}},{"method":"DELETE","path":"/api/v1/terminal/sessions/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/terminal/sessions/{id}/control","responses":{"200":{"$ref":"#/components/schemas/TerminalControlResponse"}}},{"method":"GET","path":"/api/v1/terminal/sessions/{id}/directory","responses":{"200":{"$ref":"#/components/schemas/TerminalDirectory"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/forwards","requestRequired":true,"request":{"$ref":"#/components/schemas/StartTerminalForwardRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"DELETE","path":"/api/v1/terminal/sessions/{id}/forwards/{forwardId}","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/host-key","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalHostKeyAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/password-save","requestRequired":true,"request":{"$ref":"#/components/schemas/TerminalPasswordSaveAnswer"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/reconnect","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/reconnect/stop","responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"POST","path":"/api/v1/terminal/sessions/{id}/stream","responses":{"201":{"$ref":"#/components/schemas/TerminalStreamTicket"}}},{"method":"PUT","path":"/api/v1/terminal/sessions/{id}/title","requestRequired":true,"request":{"$ref":"#/components/schemas/SetTerminalSessionTitleRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"PUT","path":"/api/v1/terminal/sessions/{id}/workspace","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalSessionRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalSessionList"}}},{"method":"GET","path":"/api/v1/terminal/shell-profiles","responses":{"200":{"$ref":"#/components/schemas/LocalShellProfileList"}}},{"method":"GET","path":"/api/v1/terminal/workspaces","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"POST","path":"/api/v1/terminal/workspaces","requestRequired":true,"request":{"$ref":"#/components/schemas/CreateTerminalWorkspaceRequest"},"responses":{"201":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"DELETE","path":"/api/v1/terminal/workspaces/{id}","responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"PATCH","path":"/api/v1/terminal/workspaces/{id}","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspace"}}},{"method":"PUT","path":"/api/v1/terminal/workspaces/{id}/position","requestRequired":true,"request":{"$ref":"#/components/schemas/MoveTerminalWorkspaceRequest"},"responses":{"200":{"$ref":"#/components/schemas/TerminalWorkspaceList"}}},{"method":"GET","path":"/api/v1/trash","responses":{"200":{"$ref":"#/components/schemas/TrashListResponse"}}},{"method":"DELETE","path":"/api/v1/trash/{entryId}","responses":{"200":{"$ref":"#/components/schemas/PurgeTrashResponse"}}},{"method":"POST","path":"/api/v1/trash/{entryId}/restore","responses":{"200":{"$ref":"#/components/schemas/RestoreTrashResponse"},"409":{"$ref":"#/components/schemas/RestoreTrashResponse"}}},{"method":"GET","path":"/api/v1/update","responses":{"200":{"$ref":"#/components/schemas/UpdateStatus"}}},{"method":"POST","path":"/api/v1/update","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateRequest"},"responses":{"202":{"$ref":"#/components/schemas/UpdateJob"}}},{"method":"POST","path":"/api/v1/update/check","responses":{"200":{"$ref":"#/components/schemas/UpdateStatus"}}},{"method":"GET","path":"/api/v1/update/log","responses":{"200":{"$ref":"#/components/schemas/UpdateLog"}}},{"method":"POST","path":"/api/v1/update/preview","requestRequired":true,"request":{"$ref":"#/components/schemas/UpdateRequest"},"responses":{"200":{"$ref":"#/components/schemas/UpdatePreview"}}},{"method":"GET","path":"/api/v1/vpn","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"PUT","path":"/api/v1/vpn/bindings","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNBindingRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"POST","path":"/api/v1/vpn/profiles","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNProfileRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"DELETE","path":"/api/v1/vpn/profiles/{name}","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"PUT","path":"/api/v1/vpn/profiles/{name}","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNProfileRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"GET","path":"/api/v1/vpn/profiles/{name}/logs","responses":{"200":{"$ref":"#/components/schemas/VPNLogs"}}},{"method":"POST","path":"/api/v1/vpn/profiles/{name}/rename","requestRequired":true,"request":{"$ref":"#/components/schemas/VPNRenameRequest"},"responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"POST","path":"/api/v1/vpn/profiles/{name}/reveal","responses":{"200":{"$ref":"#/components/schemas/VPNSecrets"}}},{"method":"DELETE","path":"/api/v1/vpn/profiles/{name}/route","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}},{"method":"POST","path":"/api/v1/vpn/profiles/{name}/route","responses":{"200":{"$ref":"#/components/schemas/VPNOverview"}}}] as const satisfies readonly Endpoint[]; export type OpenAPISchemaName = keyof typeof componentSchemas; diff --git a/web/src/i18n/messages/en.ts b/web/src/i18n/messages/en.ts index 42fe2ddf6..94da61039 100644 --- a/web/src/i18n/messages/en.ts +++ b/web/src/i18n/messages/en.ts @@ -2396,6 +2396,39 @@ export const en = { "sync.setup.saved": "Connection details and encryption key saved.", "sync.setup.changed": "The destination changed after it was checked. Check the connection again.", + "sync.import.heading": "Import exported settings", + "sync.import.hint": + "With a file or QR code exported on a machine that is already set up, you only need to enter the encryption key.", + "sync.import.chooseFile": "Choose file", + "sync.import.scan": "Scan QR code", + "sync.import.scanLabel": "Camera reading the QR code", + "sync.import.notExport": "This file is not sync settings exported by sshc.", + "sync.import.fileReadFailed": "The file could not be read.", + "sync.import.loaded": "Settings loaded. Enter the encryption key.", + "sync.import.submit": "Import", + "sync.import.chooseAnother": "Choose other settings", + "sync.import.saved": "Settings imported.", + "sync.import.failed": "The settings could not be imported.", + "sync.import.wrongKey": "This encryption key cannot open the exported settings.", + "sync.import.rejected": "The exported settings could not be read. Export them again.", + "sync.import.targetEmpty": + "The destination in these settings has no sync data. Export the settings again on a machine that is set up.", + "sync.export.heading": "Export settings", + "sync.export.hint": + "Exports the connection details, encrypted. Another machine imports them by entering the encryption key.", + "sync.export.start": "Export", + "sync.export.qrLabel": "QR code of the sync settings", + "sync.export.qrHint": "On the other machine, open Sync, choose Scan QR code, and scan this code.", + "sync.export.saveFile": "Save to file", + "sync.export.close": "Close", + "sync.export.tooLongForQR": "These settings are too long for a QR code. Hand them over as a file.", + "sync.export.chosenKeyWarning": + "You chose this encryption key yourself. If it is short or easy to guess, anyone who obtains this file or QR code may be able to decrypt it. Hand it only to people you trust.", + "sync.export.failed": "The settings could not be exported.", + "sync.export.saveFailed": "The file could not be saved.", + "qrScanner.cameraDenied": "The camera cannot be used. Allow camera access, then try again.", + "qrScanner.cameraUnavailable": "The camera could not be started.", + "qrScanner.stop": "Cancel", "sync.role.main": "Main device (send and receive)", "sync.role.receive": "Receive-only device", "sync.role.advanced": "Advanced device roles", diff --git a/web/src/i18n/messages/ja.ts b/web/src/i18n/messages/ja.ts index e59ca8705..483fc495d 100644 --- a/web/src/i18n/messages/ja.ts +++ b/web/src/i18n/messages/ja.ts @@ -2397,6 +2397,39 @@ export const ja = { "sync.setup.saved": "接続情報と暗号化キーを保存しました。", "sync.setup.changed": "確認後に同期先の状態が変わりました。もう一度、接続を確認してください。", + "sync.import.heading": "書き出した設定を取り込む", + "sync.import.hint": + "設定済みのマシンで書き出したファイルかQRコードがあれば、暗号化キーを入力するだけで設定できます。", + "sync.import.chooseFile": "ファイルを選択", + "sync.import.scan": "QRコードを読み取る", + "sync.import.scanLabel": "QRコードを読み取るカメラ", + "sync.import.notExport": "sshcで書き出した同期の設定ではありません。", + "sync.import.fileReadFailed": "ファイルを読み込めませんでした。", + "sync.import.loaded": "設定を読み込みました。暗号化キーを入力してください。", + "sync.import.submit": "取り込む", + "sync.import.chooseAnother": "別の設定を選ぶ", + "sync.import.saved": "設定を取り込みました。", + "sync.import.failed": "設定を取り込めませんでした。", + "sync.import.wrongKey": "この暗号化キーでは、書き出した設定を開けません。", + "sync.import.rejected": "書き出した設定として読み取れませんでした。もう一度書き出してください。", + "sync.import.targetEmpty": + "この設定の同期先に同期データがありません。設定済みのマシンで、もう一度書き出してください。", + "sync.export.heading": "設定を書き出す", + "sync.export.hint": + "接続情報を暗号化して書き出します。別のマシンでは、暗号化キーを入力して取り込みます。", + "sync.export.start": "書き出す", + "sync.export.qrLabel": "同期の設定のQRコード", + "sync.export.qrHint": "別のマシンのSyncで「QRコードを読み取る」を選び、このコードを読み取ってください。", + "sync.export.saveFile": "ファイルに保存", + "sync.export.close": "閉じる", + "sync.export.tooLongForQR": "設定が長いため、QRコードにできません。ファイルで渡してください。", + "sync.export.chosenKeyWarning": + "暗号化キーを自分で決めています。短いキーや推測しやすいキーだと、このファイルやQRコードを手に入れた人に解読されるおそれがあります。信頼できる相手にだけ渡してください。", + "sync.export.failed": "設定を書き出せませんでした。", + "sync.export.saveFailed": "ファイルを保存できませんでした。", + "qrScanner.cameraDenied": "カメラを使えません。カメラの使用を許可してから、もう一度お試しください。", + "qrScanner.cameraUnavailable": "カメラを起動できませんでした。", + "qrScanner.stop": "キャンセル", "sync.role.main": "このマシンで送受信", "sync.role.receive": "このマシンでは受信のみ", "sync.role.advanced": "同期モードの詳細設定", diff --git a/web/src/licenses/catalogue.generated.json b/web/src/licenses/catalogue.generated.json index 196b8c4c0..d9c3c4f19 100644 --- a/web/src/licenses/catalogue.generated.json +++ b/web/src/licenses/catalogue.generated.json @@ -903,15 +903,11 @@ }, { "name": "Go", - "version": "go1.26.11", + "version": "go1.27.3", "category": "Runtime", "url": "https://go.dev", "license": "BSD-3-Clause", "notices": [ - { - "name": "LICENSE", - "text": "Copyright 2009 The Go Authors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google LLC nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "PATENTS", "text": "Additional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part of the Go project.\n\nGoogle hereby grants to You a perpetual, worldwide, non-exclusive,\nno-charge, royalty-free, irrevocable (except as stated in this section)\npatent license to make, have made, use, offer to sell, sell, import,\ntransfer and otherwise run, modify and propagate the contents of this\nimplementation of Go, where such license applies only to those patent\nclaims, both currently owned or controlled by Google and acquired in\nthe future, licensable by Google that are necessarily infringed by this\nimplementation of Go. This grant does not include claims that would be\ninfringed only as a consequence of further modification of this\nimplementation. If you or your agent or exclusive licensee institute or\norder or agree to the institution of patent litigation against any\nentity (including a cross-claim or counterclaim in a lawsuit) alleging\nthat this implementation of Go or any code incorporated within this\nimplementation of Go constitutes direct or contributory patent\ninfringement, or inducement of patent infringement, then any patent\nrights granted to you under this License for this implementation of Go\nshall terminate as of the date such litigation is filed.\n" @@ -1063,6 +1059,19 @@ } ] }, + { + "name": "jsqr", + "version": "1.4.0", + "category": "Web", + "url": "https://www.npmjs.com/package/jsqr", + "license": "Apache-2.0", + "notices": [ + { + "name": "LICENSE", + "text": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n" + } + ] + }, { "name": "lucide-react", "version": "1.48.0", @@ -1197,6 +1206,19 @@ } ] }, + { + "name": "rsc.io/qr", + "version": "v0.2.0", + "category": "Go", + "url": "https://pkg.go.dev/rsc.io/qr@v0.2.0", + "license": "BSD", + "notices": [ + { + "name": "LICENSE", + "text": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + } + ] + }, { "name": "scheduler", "version": "0.27.0", diff --git a/web/src/licenses/sources.generated.json b/web/src/licenses/sources.generated.json index 9d51424b1..777d06902 100644 --- a/web/src/licenses/sources.generated.json +++ b/web/src/licenses/sources.generated.json @@ -1,7 +1,7 @@ { - "go.mod": "9b0473b444d6e42555afdcbb279e074b88575576eb1e539e48424b71ed40cc6b", - "go.sum": "2d13e0c2b2ae169f7c9e80e23e665fc7352278f804de6006b8549f6670e27247", - "web/package-lock.json": "7a6a6778b4ada9af7ab18e56853e8588eed887fa7c92b50fecbe0aa9ea95f0af", + "go.mod": "f3aa4d006a3bdb90a1ddcb50aa8e2f81e98c30d3b1281cb4a594e100433616bc", + "go.sum": "f06771752d3a1fce19cfe6f7dbdd77530ea95ab05acb71fb9134efa2a3be0f8a", + "web/package-lock.json": "e0a603c304031dd0bc8500ec75912497a7e54d6d089c866896ec463d76f2aaff", "android/app/build.gradle.kts": "f747f5a1fda19249935f7de57aa819c24603ed5adb5d482f0527165690b45eb2", "LICENSE": "2175ad85766892504697fa1d2e92ab5c9515ae39a852798c0d9b377b600eb581", "web/public/fonts/OFL.txt": "30f0c136e3c88e422d0791acd97238870f9054a9729bc34cf2ff0d4ed8cac4ad", diff --git a/web/src/sync/SyncDirectionField.tsx b/web/src/sync/SyncDirectionField.tsx new file mode 100644 index 000000000..bc7de6013 --- /dev/null +++ b/web/src/sync/SyncDirectionField.tsx @@ -0,0 +1,29 @@ +import type { SyncDirection } from "../api/sync"; +import { useTranslate } from "../i18n/context"; +import { FieldRow } from "../ui/FieldRow"; +import { control } from "../ui/form"; +import { syncDirectionHintKeys } from "./syncMessageKeys"; + +// Which way this machine moves data. Each machine chooses its own, so the field is asked wherever a machine +// is set up: typed in by hand or taken from another machine's exported settings. +export function SyncDirectionField({ direction, onChange }: { + direction: SyncDirection; + onChange: (direction: SyncDirection) => void; +}) { + const t = useTranslate(); + return ( + + + + ); +} diff --git a/web/src/sync/SyncPanel.test.tsx b/web/src/sync/SyncPanel.test.tsx index 1b3078ba9..12182ea22 100644 --- a/web/src/sync/SyncPanel.test.tsx +++ b/web/src/sync/SyncPanel.test.tsx @@ -52,6 +52,8 @@ const bucketStatus = { ], }; +const exportedDocument = '{"sshcSyncSettings":1,"sealed":"c3NoYw=="}'; + const historyRevision = "a".repeat(64); const historyStatus = { checkedAt: "2026-08-25T01:55:00Z", @@ -90,6 +92,14 @@ function buildApi( status: { ...status, configured: true, keyConfigured: true }, generatedKey: "ABCD-EFGH-JKMP-QRST-VWXY-Z012", }), + exportSyncSetup: vi.fn().mockResolvedValue({ + document: exportedDocument, + keyGenerated: true, + qrRows: Array.from({ length: 21 }, () => "101010101010101010101"), + }), + importSyncSetup: vi.fn().mockResolvedValue({ + status: { ...status, configured: true, keyConfigured: true }, + }), syncPushDraft: vi.fn().mockResolvedValue({ message: "Update config", added: 0, @@ -479,6 +489,61 @@ describe("SyncPanel", () => { expect(document.body.textContent ?? "").not.toContain("the-secret"); }); + it("sets the machine up from an exported file and the encryption key, without the bucket form", async () => { + const api = buildApi(unconfigured, nothingToDo); + const { container } = render(); + + await screen.findByRole("heading", { name: "Import exported settings" }); + const chooser = container.querySelector('input[type="file"]'); + await userEvent.upload(chooser!, new File([exportedDocument], "sshc-sync-settings.json", { type: "application/json" })); + await userEvent.type(await screen.findByLabelText("Encryption key"), "AB12-CD34-EF56-GH78-JK90-MN12"); + // The bucket form steps aside, so there is one direction to choose and one way forward. + expect(screen.queryByLabelText("Endpoint")).not.toBeInTheDocument(); + await userEvent.selectOptions(screen.getByLabelText("Direction"), "pull"); + await userEvent.click(screen.getByRole("button", { name: "Import" })); + + await waitFor(() => + expect(api.importSyncSetup).toHaveBeenCalledWith({ + document: exportedDocument, + key: "AB12-CD34-EF56-GH78-JK90-MN12", + direction: "pull", + }), + ); + expect(await screen.findByText("Settings imported.")).toBeInTheDocument(); + expect(screen.queryByRole("heading", { name: "Import exported settings" })).not.toBeInTheDocument(); + expect(document.body.textContent ?? "").not.toContain("AB12-CD34"); + }); + + it("says the typed key is wrong when it does not open the exported settings", async () => { + const api = buildApi(unconfigured, nothingToDo, { + importSyncSetup: vi.fn().mockRejectedValue(new ApiError("wrong_passphrase", 403, null)), + }); + const { container } = render(); + + await screen.findByRole("heading", { name: "Import exported settings" }); + await userEvent.upload(container.querySelector('input[type="file"]')!, new File([exportedDocument], "sshc-sync-settings.json")); + await userEvent.type(await screen.findByLabelText("Encryption key"), "ZZ12-CD34-EF56-GH78-JK90-MN12"); + await userEvent.click(screen.getByRole("button", { name: "Import" })); + + expect(await screen.findByText("This encryption key cannot open the exported settings.")).toBeInTheDocument(); + // The document and the key stay, so the key can be corrected and sent again. + expect(screen.getByLabelText("Encryption key")).toHaveValue("ZZ12-CD34-EF56-GH78-JK90-MN12"); + }); + + it("exports the settings as a QR code only when asked, and puts it away on close", async () => { + const api = buildApi(configured, nothingToDo); + render(); + + await userEvent.click(await screen.findByText("Manage sync settings")); + expect(screen.queryByRole("img", { name: "QR code of the sync settings" })).not.toBeInTheDocument(); + await userEvent.click(screen.getByRole("button", { name: "Export" })); + + expect(await screen.findByRole("img", { name: "QR code of the sync settings" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Save to file" })).toBeInTheDocument(); + await userEvent.click(screen.getByRole("button", { name: "Close" })); + expect(screen.queryByRole("img", { name: "QR code of the sync settings" })).not.toBeInTheDocument(); + }); + it("offers the first push as soon as setup finishes", async () => { const api = buildApi(unconfigured, nothingToDo); render(); diff --git a/web/src/sync/SyncPanel.tsx b/web/src/sync/SyncPanel.tsx index 05916c4f0..9aa7814c4 100644 --- a/web/src/sync/SyncPanel.tsx +++ b/web/src/sync/SyncPanel.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { vaultApi, type VaultApi } from "../api/vault"; -import { syncApi, type SyncApi, type PushResponse, type SyncHistoryDiff } from "../api/sync"; +import { syncApi, type SyncApi, type PushResponse, type SyncHistoryDiff, type SyncSetupExport } from "../api/sync"; import { useLanguage, useTranslate } from "../i18n/context"; import type { MessageKey } from "../i18n/messages"; import { Button, Notice } from "../ui/surface"; @@ -11,6 +11,7 @@ import { SyncDetailsSection } from "./SyncDetailsSection"; import { useSyncStatus } from "./useSyncStatus"; import { SyncExclusionsPanel } from "./SyncExclusionsPanel"; import { useSyncSetupForm } from "./useSyncSetupForm"; +import { useSyncSetupImport } from "./useSyncSetupImport"; import { useSyncRemoteState } from "./useSyncRemoteState"; import { useSyncOperation, type SyncRunOptions } from "./useSyncOperation"; import { useSyncPullPreview } from "./useSyncPullPreview"; @@ -19,6 +20,8 @@ import { SyncPullPreviewDialog } from "./SyncPullPreviewDialog"; import { SyncErrorNotice } from "./SyncErrorNotice"; import { SyncOverviewCard } from "./SyncOverviewCard"; import { SyncSettingsSection } from "./SyncSettingsSection"; +import { SyncSetupExportSection } from "./SyncSetupExportSection"; +import { SyncSetupImportSection } from "./SyncSetupImportSection"; import { SyncTransferCard } from "./SyncTransferCard"; import { SyncUnlockCard } from "./SyncUnlockCard"; import { syncPathRefusals, syncRefusals } from "./syncRefusals"; @@ -56,6 +59,9 @@ function SyncFlowSteps() { export function SyncPanel({ api = syncPanelApi }: SyncPanelProps) { const { locale, t } = useLanguage(); const form = useSyncSetupForm(); + const importForm = useSyncSetupImport(); + // The settings sealed for another machine, while their QR code is on the screen. + const [exported, setExported] = useState(null); const [master, setMaster] = useState(""); // A freshly generated key, shown once and never stored in the browser. const [revealed, setRevealed] = useState(""); @@ -285,10 +291,45 @@ export function SyncPanel({ api = syncPanelApi }: SyncPanelProps) { /> ) : null} + {status.configured ? null : ( + + void run(() => api.importSyncSetup(importForm.request), { + apply: (next) => { + adoptAndRefresh(next.status); + importForm.reset(); + setNotice(t("sync.import.saved")); + }, + failure: t("sync.import.failed"), + // The shared wording for a wrong key speaks of the key saved on this machine; here it is the one just typed. + explain: (code) => (code === "wrong_passphrase" ? t("sync.import.wrongKey") : ""), + }) + } + /> + )} + + {/* 書き出した設定を読み込んだあとは、手で入力する欄を出さない。同じ「同期の方向」が2つ並び、 + どちらを埋めるのか分からなくなる。[別の設定を選ぶ]で戻せる。 */} + {!status.configured && importForm.document !== "" ? null : ( + void run(() => api.exportSyncSetup(), { + apply: setExported, + failure: t("sync.export.failed"), + }) + } + onClose={() => setExported(null)} + /> + )} onCheckSetup={() => void run(() => api.checkSyncSetup(form.setupInput), { apply: form.acceptSetupCheck, @@ -303,6 +344,8 @@ export function SyncPanel({ api = syncPanelApi }: SyncPanelProps) { adoptAndRefresh(next.status); setRevealed(next.generatedKey ?? ""); form.finishSetup(); + // An export on the screen was sealed from the settings just replaced. + setExported(null); setNotice(t("sync.setup.saved")); }, failure: t("sync.configureFailed"), @@ -321,6 +364,8 @@ export function SyncPanel({ api = syncPanelApi }: SyncPanelProps) { { apply: (next) => { setRevealed(chooseOwn ? "" : next.key); + // An export on the screen was sealed with the key just replaced. + setExported(null); form.setOwnKey(""); form.setConfirmHistoryLoss(false); setNotice(t("sync.keySaved")); @@ -331,6 +376,7 @@ export function SyncPanel({ api = syncPanelApi }: SyncPanelProps) { ); }} /> + )} {status.configured && status.direction !== "pull" ? ( : one // without a summary gets the browser's own "Details" toggle, which would hide the whole setup. -export function SyncSettingsSection({ status, busy, form, onCheckSetup, onCompleteSetup, onSaveKey }: { +export function SyncSettingsSection({ status, busy, form, exportSection, onCheckSetup, onCompleteSetup, onSaveKey }: { status: SyncStatus; busy: boolean; form: SyncSetupForm; + // Exporting these settings for another machine; shown with the settings it exports, once sync is set up. + exportSection: ReactNode; // Asks the engine what the bucket path holds before anything is saved. onCheckSetup: () => void; onCompleteSetup: () => void; @@ -141,24 +145,7 @@ export function SyncSettingsSection({ status, busy, form, onCheckSetup, onComple /> - - - +
{setupCheck === null ? null : ( @@ -234,6 +221,7 @@ export function SyncSettingsSection({ status, busy, form, onCheckSetup, onComple {status.configured ? ( ) : null} + {status.configured ? exportSection : null}
); if (!status.configured) return body; diff --git a/web/src/sync/SyncSetupExportSection.test.tsx b/web/src/sync/SyncSetupExportSection.test.tsx new file mode 100644 index 000000000..b2fb4373a --- /dev/null +++ b/web/src/sync/SyncSetupExportSection.test.tsx @@ -0,0 +1,53 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; +import type { SyncSetupExport } from "../api/sync"; +import { saveFile } from "../ui/saveFile"; +import { SyncSetupExportSection } from "./SyncSetupExportSection"; + +vi.mock("../ui/saveFile", () => ({ saveFile: vi.fn().mockResolvedValue(null) })); + +const exported: SyncSetupExport = { + document: '{"sshcSyncSettings":1,"sealed":"c3NoYw=="}', + keyGenerated: true, + qrRows: Array.from({ length: 21 }, () => "101010101010101010101"), +}; + +function renderSection(shown: SyncSetupExport | null) { + render( {}} onClose={() => {}} />); +} + +describe("SyncSetupExportSection", () => { + it("saves the sealed document as a JSON file", async () => { + renderSection(exported); + + await userEvent.click(screen.getByRole("button", { name: "Save to file" })); + + await waitFor(() => expect(saveFile).toHaveBeenCalledTimes(1)); + const [blob, name] = vi.mocked(saveFile).mock.calls[0]!; + expect(name).toBe("sshc-sync-settings.json"); + expect(blob.type).toBe("application/json"); + expect(await blob.text()).toBe(exported.document); + }); + + it("warns that a key the person chose may be guessed from the exported settings", () => { + renderSection({ ...exported, keyGenerated: false }); + + expect(screen.getByRole("status")).toHaveTextContent("You chose this encryption key yourself."); + }); + + it("does not warn about a generated key", () => { + renderSection(exported); + + expect(screen.queryByRole("status")).not.toBeInTheDocument(); + }); + + it("offers the file alone when the settings are too long for a QR code", () => { + const { qrRows: _tooLong, ...withoutQRCode } = exported; + renderSection(withoutQRCode); + + expect(screen.queryByRole("img")).not.toBeInTheDocument(); + expect(screen.getByText("These settings are too long for a QR code. Hand them over as a file.")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Save to file" })).toBeInTheDocument(); + }); +}); diff --git a/web/src/sync/SyncSetupExportSection.tsx b/web/src/sync/SyncSetupExportSection.tsx new file mode 100644 index 000000000..70245fac5 --- /dev/null +++ b/web/src/sync/SyncSetupExportSection.tsx @@ -0,0 +1,70 @@ +import { useEffect, useRef, useState } from "react"; +import { AndroidSaveCancelled } from "../android/native"; +import type { SyncSetupExport } from "../api/sync"; +import { useTranslate } from "../i18n/context"; +import { Icon } from "../ui/icons"; +import { PageSection } from "../ui/page"; +import { QRCode } from "../ui/QRCode"; +import { saveFile, type BrowserSave } from "../ui/saveFile"; +import { Button, Notice } from "../ui/surface"; +import { syncSetupExportFileName } from "./syncSetupExportDocument"; + +// Handing this machine's sync settings to another machine. The engine seals them with the encryption key it +// already holds, so nothing is typed here; the other machine types the key to open them. exported is the +// sealed document once the engine has made one, and stays on the screen until the person closes it. +export function SyncSetupExportSection({ busy, exported, onExport, onClose }: { + busy: boolean; + exported: SyncSetupExport | null; + onExport: () => void; + onClose: () => void; +}) { + const t = useTranslate(); + const [saveFailed, setSaveFailed] = useState(false); + // The browser may read the saved file after the click; it is let go when the next save replaces it or the + // section goes away. + const lastSave = useRef(null); + useEffect(() => () => lastSave.current?.release(), []); + + function save(document: string) { + setSaveFailed(false); + // The Android app takes a bare media type for its save dialog (NativeBridge.safeMimeType), so no charset. + void saveFile(new Blob([document], { type: "application/json" }), syncSetupExportFileName) + .then((browserSave) => { + lastSave.current?.release(); + lastSave.current = browserSave; + }) + .catch((error: unknown) => { + if (!(error instanceof AndroidSaveCancelled)) setSaveFailed(true); + }); + } + + return ( + {t("sync.export.start")} : undefined} + > + {exported === null ? null : ( +
+ {exported.keyGenerated ? null : {t("sync.export.chosenKeyWarning")}} + {exported.qrRows === undefined ? ( +

{t("sync.export.tooLongForQR")}

+ ) : ( + <> + +

{t("sync.export.qrHint")}

+ + )} + {saveFailed ? {t("sync.export.saveFailed")} : null} +
+ + +
+
+ )} +
+ ); +} diff --git a/web/src/sync/SyncSetupImportSection.test.tsx b/web/src/sync/SyncSetupImportSection.test.tsx new file mode 100644 index 000000000..1fa86959b --- /dev/null +++ b/web/src/sync/SyncSetupImportSection.test.tsx @@ -0,0 +1,92 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { SyncSetupImportSection } from "./SyncSetupImportSection"; +import { useSyncSetupImport } from "./useSyncSetupImport"; + +// The camera is replaced by a button that "sees" the QR codes a test names, in order. +let codesInView: string[] = []; +vi.mock("../ui/QRCodeScanner", () => ({ + QRCodeScanner: ({ accept, onCancel }: { accept: (text: string) => boolean; onCancel: () => void }) => ( + <> + + + + ), +})); + +const exportedDocument = '{"sshcSyncSettings":1,"sealed":"c3NoYw=="}'; + +function Section({ onImport = () => {} }: { onImport?: () => void }) { + const form = useSyncSetupImport(); + return ; +} + +function offerCamera() { + vi.stubGlobal("navigator", { ...navigator, mediaDevices: { getUserMedia: vi.fn() } }); +} + +afterEach(() => { + codesInView = []; + vi.unstubAllGlobals(); +}); + +describe("SyncSetupImportSection", () => { + it("refuses a file that is not exported sync settings and keeps asking for one", async () => { + const { container } = render(
); + + await userEvent.upload( + container.querySelector('input[type="file"]')!, + new File(['{"endpoint":"https://s3.example.com"}'], "other.json"), + ); + + expect(await screen.findByRole("alert")).toHaveTextContent("This file is not sync settings exported by sshc."); + expect(screen.queryByLabelText("Encryption key")).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Choose file" })).toBeInTheDocument(); + }); + + it("takes the settings from a scanned QR code, passing over other codes in view", async () => { + offerCamera(); + codesInView = ["https://example.com/", exportedDocument]; + render(
); + + await userEvent.click(screen.getByRole("button", { name: "Scan QR code" })); + await userEvent.click(screen.getByRole("button", { name: "Camera sees the codes" })); + + expect(await screen.findByLabelText("Encryption key")).toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Camera sees the codes" })).not.toBeInTheDocument(); + }); + + it("goes back to the choice of file or camera when scanning is stopped", async () => { + offerCamera(); + render(
); + + await userEvent.click(screen.getByRole("button", { name: "Scan QR code" })); + await userEvent.click(screen.getByRole("button", { name: "Stop scanning" })); + + expect(screen.getByRole("button", { name: "Choose file" })).toBeInTheDocument(); + }); + + it("offers only the file where the page cannot open a camera", () => { + render(
); + + expect(screen.getByRole("button", { name: "Choose file" })).toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Scan QR code" })).not.toBeInTheDocument(); + }); + + it("does not import until an encryption key is typed, and lets another document be chosen", async () => { + const onImport = vi.fn(); + const { container } = render(
); + await userEvent.upload( + container.querySelector('input[type="file"]')!, + new File([exportedDocument], "sshc-sync-settings.json"), + ); + + expect(await screen.findByRole("button", { name: "Import" })).toBeDisabled(); + await userEvent.type(screen.getByLabelText("Encryption key"), "AB12-CD34-EF56-GH78-JK90-MN12{Enter}"); + expect(onImport).toHaveBeenCalledTimes(1); + + await userEvent.click(screen.getByRole("button", { name: "Choose other settings" })); + expect(screen.getByRole("button", { name: "Choose file" })).toBeInTheDocument(); + }); +}); diff --git a/web/src/sync/SyncSetupImportSection.tsx b/web/src/sync/SyncSetupImportSection.tsx new file mode 100644 index 000000000..c3d10f768 --- /dev/null +++ b/web/src/sync/SyncSetupImportSection.tsx @@ -0,0 +1,84 @@ +import { useRef } from "react"; +import { useTranslate } from "../i18n/context"; +import { FieldRow } from "../ui/FieldRow"; +import { Icon } from "../ui/icons"; +import { PageSection } from "../ui/page"; +import { PasswordInput } from "../ui/PasswordField"; +import { QRCodeScanner } from "../ui/QRCodeScanner"; +import { Button, Notice } from "../ui/surface"; +import { cameraAvailable } from "../ui/useQRCodeCamera"; +import { SyncDirectionField } from "./SyncDirectionField"; +import type { SyncSetupImport } from "./useSyncSetupImport"; + +// Setting this machine up from the settings another machine exported: a file or a QR code, then the +// encryption key. The engine opens the document with the key, checks the bucket, and saves both. +export function SyncSetupImportSection({ busy, form, onImport }: { + busy: boolean; + form: SyncSetupImport; + onImport: () => void; +}) { + const t = useTranslate(); + const chooser = useRef(null); + return ( + + {form.document === "" ? ( +
+ {form.scanning ? ( + + ) : ( +
+ {/* 拡張子を含む accept にする。Android は .json の MIME type が当てにならず、MIME type だけで + 絞ると保存したファイルを選べないことがある(FileChooser.mimeTypes)。 */} + { + const file = event.target.files?.[0]; + event.target.value = ""; + if (file !== undefined) void form.readFile(file); + }} + /> + + {cameraAvailable() ? ( + + ) : null} +
+ )} + {form.fileProblem === null ? null : {t(form.fileProblem)}} +
+ ) : ( +
{ + event.preventDefault(); + onImport(); + }} + > + {t("sync.import.loaded")} +
+ + + + +
+
+ + +
+
+ )} +
+ ); +} diff --git a/web/src/sync/syncRefusals.ts b/web/src/sync/syncRefusals.ts index 6ac009ea1..8bddb5793 100644 --- a/web/src/sync/syncRefusals.ts +++ b/web/src/sync/syncRefusals.ts @@ -44,6 +44,8 @@ export const syncRefusals: Record = { sync_ignore_invalid: "sync.exclusions.invalid", sync_setup_target_changed: "sync.setup.changed", sync_setup_target_incomplete: "sync.setup.incomplete", + sync_setup_target_empty: "sync.import.targetEmpty", + sync_settings_export_rejected: "sync.import.rejected", sync_local_changed: "sync.localChanged", sync_workspace_busy: "sync.workspaceBusy", sync_pending_transaction: "sync.pendingTransaction", diff --git a/web/src/sync/syncSetupExportDocument.test.ts b/web/src/sync/syncSetupExportDocument.test.ts new file mode 100644 index 000000000..19bcdefc1 --- /dev/null +++ b/web/src/sync/syncSetupExportDocument.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import { looksLikeSyncSetupExport } from "./syncSetupExportDocument"; + +describe("looksLikeSyncSetupExport", () => { + it("accepts the document the engine exports", () => { + expect(looksLikeSyncSetupExport('{"sshcSyncSettings":1,"sealed":"c3NoYw=="}\n')).toBe(true); + }); + + it.each([ + ["another QR code", "https://example.com/"], + ["other JSON", '{"endpoint":"https://s3.example.com"}'], + ["a JSON value that is not an object", "null"], + ["a document longer than the engine accepts", `{"sshcSyncSettings":1,"sealed":"${"A".repeat(16 * 1024)}"}`], + ])("passes over %s", (_name, text) => { + expect(looksLikeSyncSetupExport(text)).toBe(false); + }); +}); diff --git a/web/src/sync/syncSetupExportDocument.ts b/web/src/sync/syncSetupExportDocument.ts new file mode 100644 index 000000000..3a7b9c5da --- /dev/null +++ b/web/src/sync/syncSetupExportDocument.ts @@ -0,0 +1,23 @@ +// The document a set-up machine exports so that another can join the same sync target: the bucket settings and +// credentials sealed with the encryption key (internal/remotesync/settings_export.go). The page never opens it; +// it only tells such a document from something else before handing it to the engine. + +export const syncSetupExportFileName = "sshc-sync-settings.json"; + +// The engine refuses a longer document (remotesync.MaxSettingsExportLength). +const maxSyncSetupExportLength = 16 * 1024; + +export function looksLikeSyncSetupExport(text: string): boolean { + if (text.length > maxSyncSetupExportLength) return false; + try { + const parsed: unknown = JSON.parse(text); + return ( + typeof parsed === "object" && + parsed !== null && + typeof (parsed as { sshcSyncSettings?: unknown }).sshcSyncSettings === "number" && + typeof (parsed as { sealed?: unknown }).sealed === "string" + ); + } catch { + return false; + } +} diff --git a/web/src/sync/useSyncSetupForm.ts b/web/src/sync/useSyncSetupForm.ts index 750776ee4..4837f7413 100644 --- a/web/src/sync/useSyncSetupForm.ts +++ b/web/src/sync/useSyncSetupForm.ts @@ -109,6 +109,7 @@ export function useSyncSetupForm() { setState((form) => ({ ...form, ownKey: "", + chooseOwn: false, accessKeyId: "", secretAccessKey: "", setupCheck: null, diff --git a/web/src/sync/useSyncSetupImport.ts b/web/src/sync/useSyncSetupImport.ts new file mode 100644 index 000000000..953a12a8f --- /dev/null +++ b/web/src/sync/useSyncSetupImport.ts @@ -0,0 +1,61 @@ +import { useState } from "react"; +import type { SyncDirection, SyncSetupImportRequest } from "../api/sync"; +import type { MessageKey } from "../i18n/messages"; +import { looksLikeSyncSetupExport } from "./syncSetupExportDocument"; + +type SetupImportState = { + // The exported document, once a file or a QR code has given one. Empty until then. + document: string; + key: string; + direction: SyncDirection; + scanning: boolean; + // Why the chosen file was not taken. + fileProblem: MessageKey | null; +}; + +const initialState: SetupImportState = { document: "", key: "", direction: "both", scanning: false, fileProblem: null }; + +// What the person has given so far to set this machine up from another machine's exported settings. +export function useSyncSetupImport() { + const [state, setState] = useState(initialState); + + async function readFile(file: File) { + let text: string; + try { + text = await file.text(); + } catch { + setState((current) => ({ ...current, fileProblem: "sync.import.fileReadFailed" })); + return; + } + setState((current) => + looksLikeSyncSetupExport(text) + ? { ...current, document: text, fileProblem: null } + : { ...current, fileProblem: "sync.import.notExport" }, + ); + } + + // Takes the text of a QR code the camera saw. Any other code in view is passed over, so the camera keeps + // looking; true ends the scan. + function acceptScannedText(text: string): boolean { + if (!looksLikeSyncSetupExport(text)) return false; + setState((current) => ({ ...current, document: text, scanning: false, fileProblem: null })); + return true; + } + + const request: SyncSetupImportRequest = { document: state.document, key: state.key, direction: state.direction }; + + return { + ...state, + request, + readFile, + acceptScannedText, + startScanning: () => setState((current) => ({ ...current, scanning: true, fileProblem: null })), + stopScanning: () => setState((current) => ({ ...current, scanning: false })), + setKey: (key: string) => setState((current) => ({ ...current, key })), + setDirection: (direction: SyncDirection) => setState((current) => ({ ...current, direction })), + // Forgets the document and the key: after a successful import, or to choose another document. + reset: () => setState(initialState), + }; +} + +export type SyncSetupImport = ReturnType; diff --git a/web/src/ui/QRCode.test.tsx b/web/src/ui/QRCode.test.tsx new file mode 100644 index 000000000..c14df11ca --- /dev/null +++ b/web/src/ui/QRCode.test.tsx @@ -0,0 +1,13 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { QRCode } from "./QRCode"; + +describe("QRCode", () => { + it("draws each dark module one unit square, inside a margin of four modules", () => { + render(); + + const code = screen.getByRole("img", { name: "Sync settings" }); + expect(code).toHaveAttribute("viewBox", "0 0 10 10"); + expect(code.querySelector("path")).toHaveAttribute("d", "M4 4h1v1h-1zM5 5h1v1h-1z"); + }); +}); diff --git a/web/src/ui/QRCode.tsx b/web/src/ui/QRCode.tsx new file mode 100644 index 000000000..298051d8e --- /dev/null +++ b/web/src/ui/QRCode.tsx @@ -0,0 +1,24 @@ +// QRコードの周りに空ける余白(マスの数)。規格が、読み取りのために 4 マス以上を求める。 +const quietZoneModules = 4; + +// テーマの色を使わず、どのテーマでも白地に黒で描く。カメラが読むのは明暗の差で、 +// ダークのテーマの色(暗い地に明るいマス)は読めない読み取り機がある。 +const lightModuleColour = "#ffffff"; // palette-exempt +const darkModuleColour = "#000000"; // palette-exempt + +// QRCode draws the modules of a QR code. rows are its lines from the top, each a run of "1" (dark) and +// "0" (light), without the margin. +export function QRCode({ rows, label, className = "" }: { rows: readonly string[]; label: string; className?: string }) { + const side = rows.length + quietZoneModules * 2; + const darkModules = rows + .flatMap((row, y) => + [...row].map((module, x) => (module === "1" ? `M${x + quietZoneModules} ${y + quietZoneModules}h1v1h-1z` : "")), + ) + .join(""); + return ( + + + + + ); +} diff --git a/web/src/ui/QRCodeScanner.test.tsx b/web/src/ui/QRCodeScanner.test.tsx new file mode 100644 index 000000000..e4e3d93ef --- /dev/null +++ b/web/src/ui/QRCodeScanner.test.tsx @@ -0,0 +1,90 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { QRCodeScanner } from "./QRCodeScanner"; + +// What the decoder "reads" from each frame, in order; the last value repeats. +let frames: (string | null)[] = []; +vi.mock("jsqr", () => ({ + default: () => { + const text = frames.length > 1 ? frames.shift() : frames[0]; + return text === null || text === undefined ? null : { data: text }; + }, +})); + +function fakeCamera() { + const stop = vi.fn(); + const getUserMedia = vi.fn().mockResolvedValue({ getTracks: () => [{ stop }] }); + vi.stubGlobal("navigator", { ...navigator, mediaDevices: { getUserMedia } }); + return { getUserMedia, stop }; +} + +beforeEach(() => { + // jsdom has neither a playing