From e4a21090a724c857f68661ae9388b5256d220676 Mon Sep 17 00:00:00 2001 From: fylorn <249551762+fylorn@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:38:34 +0800 Subject: [PATCH 1/3] Lite docs: sync and backup A new page, Sync and backup, in English and Chinese: configuration packs, backups on the device and in the sync location, exporting and importing with keys optional, passphrase encryption and per-item conflict choices, turning on sync with a folder, WebDAV or S3-compatible storage and how to set up iCloud Drive, Dropbox, OneDrive, Jianguoyun, Nextcloud, Synology, Cloudflare R2, Alibaba Cloud OSS, Tencent Cloud COS and MinIO, encryption, what is synced, conflicts, sync states, restoring all or part of a version, connected clients, remote cores, the twcore commands, and what is never exported or synced. Import links gains "Generated by Lite" for the upstream Share dialog. Features, the overview, remote core, architecture and the FAQ mention sync and backup where it changes what they say. Co-Authored-By: Claude Opus 5.5 --- src/content/docs-lite/en/architecture.md | 2 +- src/content/docs-lite/en/faq.md | 4 +- src/content/docs-lite/en/features.md | 10 +- src/content/docs-lite/en/import-links.md | 24 +- src/content/docs-lite/en/overview.md | 3 +- src/content/docs-lite/en/remote-core.md | 2 +- src/content/docs-lite/en/sync-and-backup.md | 338 ++++++++++++++++++ src/content/docs-lite/zh-CN/architecture.md | 2 +- src/content/docs-lite/zh-CN/faq.md | 4 +- src/content/docs-lite/zh-CN/features.md | 10 +- src/content/docs-lite/zh-CN/import-links.md | 24 +- src/content/docs-lite/zh-CN/overview.md | 3 +- src/content/docs-lite/zh-CN/remote-core.md | 2 +- .../docs-lite/zh-CN/sync-and-backup.md | 338 ++++++++++++++++++ src/content/docs/_meta.ts | 18 +- 15 files changed, 764 insertions(+), 20 deletions(-) create mode 100644 src/content/docs-lite/en/sync-and-backup.md create mode 100644 src/content/docs-lite/zh-CN/sync-and-backup.md diff --git a/src/content/docs-lite/en/architecture.md b/src/content/docs-lite/en/architecture.md index 2b9f4aa..5198c62 100644 --- a/src/content/docs-lite/en/architecture.md +++ b/src/content/docs-lite/en/architecture.md @@ -10,7 +10,7 @@ src-tauri/ Tauri 2 shell: supervises core, renders the menu bar and the t src-tauri/crates/ client setup (tw-adopt) and the configuration scan (tw-scan) ``` -- **`src-tauri/`** is the Tauri 2 shell. It starts ThinkWatch Core and supervises it: a core that exits is restarted, and after repeated failed starts it runs in a safe mode in which only its control plane is up, so that configuration, history and rollback stay available. The shell renders the menu bar on macOS, the notification-area icon and menu on Windows, and the tray icon and menu on Linux, and it delivers system notifications. Launch at login starts the app, which then starts core; core is not registered on its own. +- **`src-tauri/`** is the Tauri 2 shell. It starts ThinkWatch Core and supervises it: a core that exits is restarted, and after repeated failed starts it runs in a safe mode in which only its control plane is up, so that the configuration and restoring from backups stay available. The shell renders the menu bar on macOS, the notification-area icon and menu on Windows, and the tray icon and menu on Linux, and it delivers system notifications. Launch at login starts the app, which then starts core; core is not registered on its own. - **`src-tauri/crates/`** holds `tw-adopt`, which points clients at the gateway and restores them, and `tw-scan`, which scans client configuration for the MCP page. Both act on the computer the app runs on, also while the app is connected to a core on a server. - **`src/`** is the frontend, written in React 19 and Tailwind 4. diff --git a/src/content/docs-lite/en/faq.md b/src/content/docs-lite/en/faq.md index 1619787..de4778f 100644 --- a/src/content/docs-lite/en/faq.md +++ b/src/content/docs-lite/en/faq.md @@ -46,9 +46,9 @@ ThinkWatch Lite changes a client's configuration when the client is connected on ## Where does ThinkWatch Lite store data, and what does it connect to? -Configuration, keys and request history stay on the computer: in `~/.thinkwatch` on macOS and Linux, in `%APPDATA%\ThinkWatch` for the Windows installer, and in the `data\` folder next to the program for the Windows portable copy. The gateway listens on `127.0.0.1` port 8788 by default. +Configuration, keys and request history stay on the computer: in `~/.thinkwatch` on macOS and Linux, in `%APPDATA%\ThinkWatch` for the Windows installer, and in the `data\` folder next to the program for the Windows portable copy. With sync turned on, a copy of the configuration, keys included, is also kept in the sync location the user chose, encrypted with the sync passphrase by default. The gateway listens on `127.0.0.1` port 8788 by default. -Besides the upstreams it forwards requests to, ThinkWatch Lite connects to GitHub to check for updates and to refresh LiteLLM's public price list once a day. Signing in to a ChatGPT or Z.ai account goes through their sign-in pages, and a remote core is contacted only when one is configured. +Besides the upstreams it forwards requests to, ThinkWatch Lite connects to GitHub to check for updates and to refresh LiteLLM's public price list once a day. Signing in to a ChatGPT or Z.ai account goes through their sign-in pages, a remote core is contacted only when one is configured, and a sync location only when sync is turned on. ## Does ThinkWatch Lite collect usage data? diff --git a/src/content/docs-lite/en/features.md b/src/content/docs-lite/en/features.md index 36f8398..3129349 100644 --- a/src/content/docs-lite/en/features.md +++ b/src/content/docs-lite/en/features.md @@ -61,7 +61,7 @@ Each upstream is also compared over the last 7 days with the others serving the API keys and header values can be written as `${NAME}` to read a system environment variable. On macOS these come from the login shell, so variables exported in `~/.zshrc` and similar files apply, and the same holds on Linux (`~/.bashrc`, `~/.profile` and so on); on Windows they are the environment variables configured in system settings. After a variable changes, reopening the app picks it up. Proxy variables such as `HTTPS_PROXY`, and `PATH`, are not read. -A relay, a vendor or a ThinkWatch Enterprise console can hand out an import link, `thinkwatch://import?…` or its web form `https://thinkwat.ch/import#…`, that opens the New upstream dialog with the service, address, key, headers, concurrency limit, models, model specs and a price sheet already filled in. The dialog names the host that will receive requests and keys, and nothing is written or sent before Next is chosen or a sign-in is started. A link only ever adds one upstream and cannot set proxies, aliases, routes or environment variable references; a link that needs a newer version of the app says so. The parameters for each service and a link builder are in [Import links](/docs/lite/import-links). +A relay, a vendor or a ThinkWatch Enterprise console can hand out an import link, `thinkwatch://import?…` or its web form `https://thinkwat.ch/import#…`, that opens the New upstream dialog with the service, address, key, headers, concurrency limit, models, model specs and a price sheet already filled in. The dialog names the host that will receive requests and keys, and nothing is written or sent before Next is chosen or a sign-in is started. A link only ever adds one upstream and cannot set proxies, aliases, routes or environment variable references; a link that needs a newer version of the app says so. The parameters for each service and a link builder are in [Import links](/docs/lite/import-links). **Share…** in an upstream row's menu writes an import link for an existing upstream, with or without its key, and **Export selected…** on the toolbar exports the selected upstreams as a configuration pack (see [Sync and backup](#sync-and-backup)). ## Routing and failover @@ -105,7 +105,13 @@ Plugins are short JavaScript files that change requests before they go to an ups ## Settings -Settings has seven sections. Connection lists the local core and the saved remote cores, described in [Connecting to a remote core](/docs/lite/remote-core). General sets the language, the appearance, what the menu bar item shows on macOS (or that it is hidden), launch at login, whether notices arrive as system notifications, in the app only or not at all, and shows hidden guidance hints and ignored warnings again. Listening sets who can reach the gateway (this machine only, the local network of a chosen interface, or every interface), its port and the allowed address ranges. Failover sets how long a failing upstream is paused before requests go to the next one: after how many consecutive failures, for how long, and separate pauses for an insufficient balance, a used-up quota and rate limits. It also sets the no-response timeout, after which an upstream that has sent no content is given up on (300 seconds by default, from 30 to 3,600), and how long in total a request may wait for a free slot on a full upstream or for a key's per-minute or per-hour limit, 30 seconds by default. Log retention sets how long request payloads and request records are kept, and a size cap for payloads; the security log expires with the request records. About shows the version, checks for updates and produces a diagnostics bundle with keys and addresses masked. Uninstall restores every connected client and removes the autostart entry, and is meant to be run before the app is deleted. +Settings has eight sections. Connection lists the local core and the saved remote cores, described in [Connecting to a remote core](/docs/lite/remote-core). General sets the language, the appearance, what the menu bar item shows on macOS (or that it is hidden), launch at login, whether notices arrive as system notifications, in the app only or not at all, and shows hidden guidance hints and ignored warnings again. Listening sets who can reach the gateway (this machine only, the local network of a chosen interface, or every interface), its port and the allowed address ranges. Failover sets how long a failing upstream is paused before requests go to the next one: after how many consecutive failures, for how long, and separate pauses for an insufficient balance, a used-up quota and rate limits. It also sets the no-response timeout, after which an upstream that has sent no content is given up on (300 seconds by default, from 30 to 3,600), and how long in total a request may wait for a free slot on a full upstream or for a key's per-minute or per-hour limit, 30 seconds by default. Log retention sets how long request payloads and request records are kept, and a size cap for payloads; the security log expires with the request records. Sync and backup exports and imports the configuration, turns on sync and lists the versions of the configuration that can be restored (see [Sync and backup](#sync-and-backup)). About shows the version, checks for updates and produces a diagnostics bundle with keys and addresses masked. Uninstall restores every connected client and removes the autostart entry, and is meant to be run before the app is deleted. + +## Sync and backup + +A new computer, a reinstall or a second device does not mean entering the configuration again: it can be exported to one file and imported, or synced between devices automatically through a folder, WebDAV or S3-compatible storage of the user's choosing, encrypted with a passphrase by default. Every change to the configuration leaves a version that can be restored, and a deleted upstream can be brought back on its own. + +The export and import options, how to set up each sync location, conflicts and restoring are described in [Sync and backup](/docs/lite/sync-and-backup). ## Menu bar, system tray and notifications diff --git a/src/content/docs-lite/en/import-links.md b/src/content/docs-lite/en/import-links.md index c8316d2..58b4de8 100644 --- a/src/content/docs-lite/en/import-links.md +++ b/src/content/docs-lite/en/import-links.md @@ -2,7 +2,7 @@ ThinkWatch Lite accepts links that open the New upstream dialog with the settings already filled in. A relay, a model vendor or a ThinkWatch Enterprise console can place such a link in its dashboard or in a welcome message. The user opens it, checks the settings in the same dialog used to add an upstream by hand, and creates the upstream with a few clicks. -This page is written for relay and vendor operators. It describes the two forms of the link, the parameters grouped the way the dialog groups them, which service takes which parameter, the encodings, and what a link intentionally cannot set. A link builder is at the end of the page. +This page is written for relay and vendor operators. It describes the two forms of the link, the parameters grouped the way the dialog groups them, which service takes which parameter, the encodings, and what a link intentionally cannot set. A link builder is at the end of the page. ThinkWatch Lite can also write such a link for an existing upstream; see [Generated by Lite](#generated-by-lite). The parameters other than `name`, `url`, `protocol`, `key` and `models` need ThinkWatch Lite 2026.10.16 or later. A link that uses only those five also opens in earlier versions, and every link written for earlier versions stays valid. @@ -213,6 +213,28 @@ thinkwatch://import?service=newapi&url={address}&key={key} The site fills in its server address and the key of the row, and the preset appears in the chat menu of each key. Opening it needs ThinkWatch Lite 2026.10.16 or later; with an earlier version, or without the app, nothing happens. +## Generated by Lite + +ThinkWatch Lite can also write an import link for an existing upstream, to hand it to another device or another person. In the ⋯ menu of an upstream row, **Share…** follows **View traffic** and opens the share dialog: + +- The dialog shows the link in its web form. **Copy web link** and **Copy thinkwatch:// link** copy each form. On a device without Lite, the web link shows how to install it, and nothing after `#` is sent to the site's server. +- **Include keys** is on by default, and the dialog then notes: “The link contains the key. Anyone holding the link can use the key; send it only through a trusted channel.” With it off, the link carries no key, and the recipient fills it in in the dialog. +- The foot of the dialog lists what the link includes and what it leaves out, for example “Includes: address, protocol, key, 2 models, balance source. Leaves out: outbound proxy, routes.” + +What the link carries: + +| Content | Written | Notes | +|---|---|---| +| Service, authentication method, site, name, address, protocol (`service`, `auth`, `site`, `name`, `url`, `protocol`) | Yes | — | +| Concurrency limit, client identity forwarding, balance source, model list, the models it is limited to, specs set by hand, billing mode, price multiplier, prices (`concurrency`, `client_identity`, `balance`, `models`, `models_only`, `specs`, `billing`, `price_multiplier`, `prices`) | Yes | Values equal to the defaults are left out | +| Key, headers, AWS credentials, OAuth client credentials | As set by **Include keys**, on by default | A value written as `${NAME}` cannot be written, since links do not accept that form; it is left out, with the reason | +| Outbound proxy, aliases, routes, weights, the disabled state, AWS profile | No | Import links do not accept them; see [What a link can never set](#what-a-link-can-never-set) | +| ChatGPT and Z.ai / BigModel account sign-ins | No | The recipient stops at the sign-in step and signs in | + +When the link would be longer than 8192 characters, usually because of many prices or specs, the link form is unavailable and the dialog suggests a configuration pack instead. **Export as configuration pack…** opens the export dialog with the upstream checked, with passphrase encryption optional and off by default. The recipient imports it with **Import configuration…**, and the pack can also carry what a link cannot set, such as the upstream's outbound proxy; see [Sync and backup](/docs/lite/sync-and-backup#exporting). + +Links are written by the same rules they are read by: a generated link opened in the same version of the app fills in the New upstream dialog with the settings of the original upstream, apart from what the table above leaves out. + ## Keys in links A link that contains a key, a refresh token or a secret access key is a credential. Generate a separate link for each customer and deliver it through a private channel, such as the customer's own dashboard. A link without a key leaves the key field empty, to be filled in the dialog. diff --git a/src/content/docs-lite/en/overview.md b/src/content/docs-lite/en/overview.md index f8f38d7..b772e5d 100644 --- a/src/content/docs-lite/en/overview.md +++ b/src/content/docs-lite/en/overview.md @@ -13,6 +13,7 @@ ThinkWatch Lite is a local gateway for Claude Code, Codex and other AI clients, - **Every request traceable.** The matched rule, each attempt, any format conversion and the cost, with replay against another upstream; the whole history can be searched, including the text of requests and answers. - **Routing and failover.** Rules by model, tools, images and more; groups that fail over before the answer begins and keep each session on one upstream. - **Any upstream.** API keys, Amazon Bedrock, ChatGPT and Z.ai accounts, relays and local models, with conversion between the Anthropic, OpenAI and Gemini APIs. +- **Sync and backup.** The configuration can be exported to one file and imported on a new computer or after a reinstall, or synced between devices through a cloud drive folder, WebDAV or S3-compatible storage, encrypted by default. Every change to it leaves a version that can be restored. - **Costs stated as they are.** Estimates marked, unpriced requests counted separately rather than as zero. ## Pages @@ -28,7 +29,7 @@ ThinkWatch Lite is a local gateway for Claude Code, Codex and other AI clients, | Security | The security log and the three protections with their rules | | MCP | MCP servers, skills and hooks in each client, and the configuration scan | | Plugins | JavaScript plugins that change requests and answers, with their permissions, trial runs and logs | -| Settings | Connection, language, appearance, menu bar, notifications, listening, retention, updates and uninstall | +| Settings | Connection, language, appearance, menu bar, notifications, listening, retention, sync and backup, updates and uninstall | Each page is described in [Features](/docs/lite/features). diff --git a/src/content/docs-lite/en/remote-core.md b/src/content/docs-lite/en/remote-core.md index 57b472b..88781d2 100644 --- a/src/content/docs-lite/en/remote-core.md +++ b/src/content/docs-lite/en/remote-core.md @@ -51,7 +51,7 @@ Clients on this computer, including those inside WSL on Windows, that point to t - **Overview, Traffic, Keys, Upstreams, Routing and Security** show and change the configuration and history of the core on the server. - **Clients and MCP** still act on the computer the app runs on: the Clients page points this computer's clients at the server's gateway, and the MCP page checks this computer's MCP servers, skills and hooks. A note at the top of each page says so. Clients on other machines are configured by hand with the server's gateway address and a gateway key. -- **Settings** is divided into the app on this computer (connection, general settings, about, uninstall) and the configuration on the server (listening, log retention). The remote control port and its key can be changed only on the server. +- **Settings** is divided into the app on this computer (connection, general settings, about, uninstall) and the configuration on the server (listening, failover, log retention, sync and backup). The server's configuration can be exported, imported and restored, and the server can join a sync; see [Sync and backup](/docs/lite/sync-and-backup#with-a-remote-core). The remote control port and its key can be changed only on the server. - **ChatGPT sign-in** uses a device code only, with the verification address and the code each ready to copy: a browser sign-in returns to the machine that runs core, which is the server. - `${NAME}` in an upstream's key or headers reads the environment of the core process on the server, and the **system proxy** option means the server's system proxy. - The **diagnostics bundle** is not available. The server refuses three things over a remote connection: producing the bundle, stopping core, and changing `listen.control`. diff --git a/src/content/docs-lite/en/sync-and-backup.md b/src/content/docs-lite/en/sync-and-backup.md new file mode 100644 index 0000000..328273d --- /dev/null +++ b/src/content/docs-lite/en/sync-and-backup.md @@ -0,0 +1,338 @@ +# Sync and backup + +After moving to a new computer or reinstalling, the ThinkWatch Lite configuration can be imported whole from one file instead of being entered again item by item. Several computers and cores on servers can also share one configuration through a sync location of their own choosing: an upstream added or a key rotated on one device reaches the others. Every change to the configuration leaves a version that can be restored. + +This page covers configuration packs, backups, exporting and importing, turning on sync and setting up each kind of sync location, encryption, conflicts, restoring, and what is never exported or synced. Sharing a single upstream is described in [Import links](/docs/lite/import-links#generated-by-lite). + +It is gathered in **Settings › Sync and backup**, which has a **Sync** part and a **Backups** part. While the app is connected to a remote core, the section sits under the server's configuration and manages the configuration on the server; see [With a remote core](#with-a-remote-core). + +## Configuration packs + +A configuration pack is a single file with the `.twconfig` extension that holds the configuration, the plugin scripts and Lite's general preferences. Exports, shares, the backup versions on the device and the versions in a sync location all use it. + +- An unencrypted pack is a ZIP file. A pack encrypted with a passphrase is in the age format, with the key derived from the passphrase by scrypt, and decrypts to the same ZIP file. Both have the same extension and are told apart by their header. +- A pack is at most 16 MiB and may hold only the expected kinds of file; any other path, an absolute path or `..` rejects the whole pack. A plugin file whose SHA-256 does not match the configuration's record of it is marked as an error. +- A pack exported by a newer version that this version cannot read is reported with “This configuration pack was exported by a newer version. Update ThinkWatch Lite first.” A pack from an older version is read as the current version reads it; fields that fail are listed in the preview and can be removed by the rules of **Repair** before importing. +- A wrong passphrase is reported as “Incorrect passphrase”. + +## Backups + +Backups are earlier versions of the configuration, kept in two places: + +| Where | When a version is made | Kept | +|---|---|---| +| This device | On every change to the configuration: in the app, from the command line, by editing the file by hand, by sync, import, restore or **Repair**. Not when the content equals the previous version, and not when a ChatGPT token is renewed. | The last 10 versions | +| Sync location | The versions each device uploads once sync is on | The latest and the previous version | + +A version on the device is a complete configuration pack, with the plugins, ChatGPT sign-in tokens and local settings, stored in `history/` in the data directory. The folder and files can be read only by their owner and are not encrypted, the same protection `config.yaml` itself has. These versions replace the former change history, and the history entry of the configuration file editor now opens the backup list. + +The **Backups** part lists the versions newest first: the time, the source (such as **Changed in the app**, **Imported**, **File edited by hand** or **Synced from MacBook Air**), where it is stored (this device, the sync location), what it holds (such as “7 upstreams, 2 plugins”) and the Core version that made it; the version in use is marked **Current**. A version stored both on the device and in the sync location is one row with both places marked. Every other version has **Restore…**, described under [Restore](#restore), and **Save as…**, which saves it as a configuration pack, optionally encrypted with a passphrase. + +ThinkWatch Lite makes no scheduled backups and has no separate manual backup. To keep a copy elsewhere, use **Export configuration…** or **Save as…** on a version; the file can be stored anywhere. + +## Exporting + +There are three ways to export: + +- **Export configuration…** in the **Backups** part, which exports the whole configuration by default; +- **Export selected…** on the toolbar of the Upstreams page, once upstreams are selected; +- **Export as configuration pack…** in the share dialog of an upstream. + +All three open the same export dialog. From the Upstreams page, the scope starts as **Selected items** with those upstreams checked. + +| Item | Description | +|---|---| +| Scope | **Whole configuration** or **Selected items**. Selected items lists the upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, security rules, plugins and global settings by category. Items that the selected ones refer to are checked automatically and marked **Dependency**, such as the upstream of a route or the proxy of an upstream. | +| Include keys | On by default. Covers upstream keys, header values, AWS credentials, proxy passwords, gateway keys and the keys obtained by signing in to a Z.ai / BigModel account. Values written as `${NAME}` are exported as written, not expanded. | +| Encrypt with passphrase | Off by default. When on, the passphrase is entered twice and has at least 8 characters. It is not stored, and importing asks for the same passphrase. | +| Not exported | What this export leaves out, such as “ChatGPT sign-in (1 upstream); sign in again after importing”, the listening, remote control and sync settings, and the request records. | + +With keys included and no encryption, the foot of the dialog warns: “The file contains keys in plain text. Keep it safe.” + +## Importing + +There are three ways to import: + +- **Import configuration…** in the **Backups** part; +- dropping a configuration pack onto the main window; +- on a new installation, whose configuration is still the initial one, **Import configuration…** next to **New upstream** on the empty Upstreams page. + +An encrypted pack first asks for its passphrase. The dialog then states what the pack is (whole or partial, with or without keys, when it was exported and by which Core version) and lists each of its items by category with a status: + +| Status | When | What happens | +|---|---|---| +| New | Nothing of that name exists | Imported; uncheck it to skip it | +| Same | The same name with the same content | Nothing; counted in the summary | +| Conflict | The same name with different content | **Overwrite**, **Import renamed** or **Skip** must be chosen; there is no default | +| Conflict (global settings) | That section was changed on this device and differs from the pack | **Overwrite** or **Skip** must be chosen | +| Key missing | The pack holds no keys | Imported disabled, with a **Key missing** tag on the upstream row; it can be enabled once the key is filled in | +| Sign-in required | A ChatGPT upstream | Imported disabled, with **Sign in…** on the upstream row | +| Plugin | A plugin script | Imported disabled; a plugin that may change tool calls is approved when it is turned on | +| Error | It refers to an AWS profile, proxy or upstream that does not exist here; a field is invalid; or a gateway key has the value of another existing key | Cannot be imported, with the reason | + +**Import** stays unavailable until every conflict has a choice. **Overwrite all**, **Import all renamed** and **Skip all** above the list settle every conflict at once. + +An import follows these rules: + +- Upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, custom security rules and plugins are added one by one, by name. Items in the current configuration that the pack does not have stay as they are; an import never deletes anything. Replacing the whole configuration with another is a restore; see [Restore](#restore). +- Each section of the global settings (failover, the state of each protection, the default route, the default key, auxiliary requests and the price sheet settings) is one item. When the section on this device is still at its defaults, the imported value is taken; otherwise a different imported value is a conflict. +- **Import renamed** proposes the original name with a number, such as “Company relay (2)”, which can be edited in the row and must not be taken. Routes, aliases and groups in the pack that refer to the item use the new name. +- **Overwrite** replaces the item of that name with the imported one. Routes and aliases on this device that refer to it stay as they are. +- A route, alias or group that refers to a skipped upstream is marked as an error, naming the missing upstream. An upstream imported under a new name does not count as missing. +- When the pack holds no keys, the imported gateway keys are generated anew. +- An import is recorded as a version in the backups, so the configuration from before it can be restored. When the configuration changes after the preview, the import asks for a new preview. + +## Turning on sync + +Sync lets several devices share one configuration. A device is one Core taking part: the local core, or a remote core on a server. Each device is connected to one sync location at a time. ThinkWatch offers no hosted sync service of its own; the sync location is chosen by the user: + +| Type | Storage | Connection settings | +|---|---|---| +| Folder | A folder synced by iCloud Drive, Dropbox, OneDrive, Google Drive, Syncthing and the like, or a NAS share | The folder path | +| WebDAV | Jianguoyun (Nutstore), Nextcloud, Synology NAS, InfiniCloud and others | Address, user name and password (or app password) | +| S3-compatible storage | Amazon S3, Cloudflare R2, Alibaba Cloud OSS, Tencent Cloud COS, MinIO and others | Endpoint, region, bucket, prefix, Access Key ID and Secret Access Key | + +Requests to WebDAV and S3-compatible storage can go out through one of the outbound proxies; the default is direct. + +While sync is off, the **Sync** part holds only **Turn on sync…** and one line: “Devices share one configuration, and the sync location keeps the latest and the previous version.” Turning sync on takes three steps: + +1. **Sync location.** Pick the type and fill in the connection settings; **Test connection** checks that the location can be read and written. An empty location leads to step 2 to set up encryption. A location that already holds sync data leads to step 2 for its sync passphrase when it is encrypted, and skips step 2 when it is not. +2. **Encryption.** Starting a new sync, choose to encrypt (the default) or not, and enter the sync passphrase twice; it has at least 12 characters. Joining an existing sync, enter the passphrase once; the devices already syncing are listed with the time each last synced. A wrong passphrase is reported as “Incorrect sync passphrase”. +3. **Merge.** Only when joining an existing sync from a device that already has a configuration. The items are previewed by the rules of [importing](#importing), and each conflict is resolved with overwrite, rename or skip; alternatively, **Replace this device's configuration with the synced data**. + +A device whose configuration is still the initial one, a new installation that has not been changed, skips step 3 and takes the synced data as it is. A new installation offers **Join sync…** on its empty Upstreams page, which opens these steps directly. The configuration from before joining is kept in the backups. The device's name starts as the computer's name and can be changed. + +Once sync is on, the **Sync** part shows the state (such as “Synced · 2 minutes ago”), the sync location with its connection settings, the outbound proxy, encryption, the upstreams not synced and this device's name, followed by the devices taking part, each with its name, last sync, Core version and state. **Sync now** checks at once. The sync settings are written to `config.yaml` and take effect when **Save** is chosen. + +### When devices sync + +| When | What happens | +|---|---| +| The configuration changes here | Changes made within 10 seconds are combined and uploaded as one new version. Before Core exits it uploads anything not yet uploaded, and what fails to upload goes up at the next start. | +| On a schedule | WebDAV and S3-compatible storage are checked for new versions every 30 minutes; a new version is downloaded, merged and applied. A folder is watched for changes, and a new version is applied as soon as it arrives. | +| On events | Core starting, the device waking from sleep, the network coming back and the main window opening each trigger a check. | +| By hand | **Sync now**. | + +Renewing a ChatGPT token, changing local settings and upgrading a built-in plugin upload nothing. With WebDAV or S3-compatible storage, a change made on another device arrives within 30 minutes, or at once when the main window opens; with a folder, it arrives as soon as the folder's own client has carried it over. A version from another device is applied through the usual configuration write, checked and then loaded without a restart, and is recorded as a backup version with **Synced from** and the device's name as its source. + +## Setting up each sync location + +Every device connects to the same location: the same folder in the same cloud drive, the same WebDAV address, or the same bucket and prefix. A new, empty folder or prefix for ThinkWatch, such as `ThinkWatch`, is recommended. + +### iCloud Drive, Dropbox, OneDrive and other folders + +Choose **Folder** and pick, in the folder dialog, the folder created for ThinkWatch in the cloud drive; pick the same folder on every device. The drive's own client carries the files between devices, and ThinkWatch only reads and writes that folder. + +| Drive | Where its folder usually is | +|---|---| +| iCloud Drive | macOS: iCloud Drive in the Finder sidebar, that is `~/Library/Mobile Documents/com~apple~CloudDocs`. Windows: `%USERPROFILE%\iCloudDrive` from iCloud for Windows | +| Dropbox | macOS: `~/Library/CloudStorage/Dropbox`, or `~/Dropbox` for older installations. Windows: `%USERPROFILE%\Dropbox`. Linux: `~/Dropbox` | +| OneDrive | macOS: the folder starting with `OneDrive` in `~/Library/CloudStorage/`. Windows: `%USERPROFILE%\OneDrive` | +| Google Drive | macOS: the folder starting with `GoogleDrive` in `~/Library/CloudStorage/`. Windows: My Drive on the Google Drive volume | +| Syncthing, NAS shares | The same folder, synced or mounted on each device | + +When the drive downloads files only on demand, a version uploaded by another device counts as not yet arrived until it is on this device, and is merged once it is. Keeping the folder always on the device, such as with OneDrive's **Always keep on this device**, makes new versions arrive as soon as they are carried over. + +ThinkWatch writes each version under a temporary name and renames it when it is complete, so a drive never carries a half-written file. A file that cannot be decrypted or fails its checksum is also treated as not yet arrived, without an error. + +### Jianguoyun (Nutstore) + +1. Create a folder in Jianguoyun, such as `ThinkWatch`. +2. In the Jianguoyun web app, open the security options of the account settings and add an application under third-party app management; Jianguoyun generates an app password for it. +3. In ThinkWatch Lite, choose **WebDAV**. The address is `https://dav.jianguoyun.com/dav/ThinkWatch/`, the user name is the Jianguoyun account, and the password is the app password from step 2, not the account password. + +### Nextcloud + +1. Create a folder in Nextcloud, such as `ThinkWatch`. +2. Create an app password on the Security page of the personal settings. An account with two-factor authentication must use an app password. +3. The address is `https:///remote.php/dav/files//ThinkWatch/`, with the Nextcloud user name and the app password from step 2. The Files settings in Nextcloud's Files app also show the WebDAV address. + +### Synology NAS + +1. Install WebDAV Server from the DSM Package Center and enable HTTPS, on port 5006 by default. +2. Create a subfolder in a shared folder, such as `ThinkWatch`. A DSM account of its own for ThinkWatch, with read and write access to that folder only, is recommended. +3. The address is `https://:5006//ThinkWatch/`, with that DSM account's user name and password. + +ThinkWatch always verifies the TLS certificate, and the self-signed certificate DSM comes with does not pass. A Let's Encrypt certificate for the NAS's domain can be obtained in DSM under **Control Panel › Security › Certificate**. For use on the local network only, `http://:5005/…` works too: plain `http://` is accepted for WebDAV only on this computer and private networks, by the same rules as [import links](/docs/lite/import-links#connection), and the password, and the configuration when it is not encrypted, then cross the local network unencrypted. + +### Cloudflare R2 + +1. Create a bucket in R2 in the Cloudflare dashboard, without public access. +2. Create an API token in R2's API token management, with object read and write permission applied to that bucket only. It gives an Access Key ID and a Secret Access Key. +3. In ThinkWatch Lite, choose **S3-compatible storage**: + +| Field | Value | +|---|---| +| Endpoint | `https://.r2.cloudflarestorage.com`, where `` is the Cloudflare account ID; for a bucket in the EU jurisdiction, `https://.eu.r2.cloudflarestorage.com` | +| Region | `auto` | +| Bucket | The bucket from step 1 | +| Prefix | Empty, or a path such as `thinkwatch/` to keep it apart from other content in the bucket | +| Access Key ID, Secret Access Key | The values from step 2 | + +### Alibaba Cloud OSS + +| Field | Value | +|---|---| +| Endpoint | The S3-compatible address of OSS, `https://s3.oss-.aliyuncs.com`; for China (Hangzhou), `https://s3.oss-cn-hangzhou.aliyuncs.com` | +| Region | The region ID, such as `cn-hangzhou` | +| Bucket | The bucket name | +| Access Key ID, Secret Access Key | The AccessKey ID and AccessKey secret of a RAM user | + +A RAM user of its own for ThinkWatch, allowed to read and write that bucket only, is recommended, and the bucket's access stays private. + +### Tencent Cloud COS + +| Field | Value | +|---|---| +| Endpoint | `https://cos..myqcloud.com`; for Guangzhou, `https://cos.ap-guangzhou.myqcloud.com` | +| Region | The region code, such as `ap-guangzhou` | +| Bucket | The full bucket name with its APPID, in the form `-` | +| Access Key ID, Secret Access Key | The SecretId and SecretKey of an API key in Cloud Access Management | + +The key of a sub-user allowed to read and write that bucket only is recommended, and the bucket's access stays private. + +### MinIO + +| Field | Value | +|---|---| +| Endpoint | The S3 API address of MinIO, such as `https://minio.example.com:9000`; the default port is 9000, not the console's port | +| Region | The region set in MinIO, or `us-east-1` when none is set | +| Bucket | A bucket created beforehand | +| Access Key ID, Secret Access Key | An access key created for ThinkWatch, with a policy that allows reading and writing that bucket only | + +### Other S3-compatible storage + +The endpoint is the service's S3 API address, `https://s3..amazonaws.com` for Amazon S3, and the region is the bucket's region. The bucket should be private, and the key should reach that bucket only. + +## Encryption + +- Encryption is chosen when sync is turned on, encrypted by default, and every device on one sync location uses the same setting. Each device asks for the sync passphrase once, when it joins. The passphrase and the sync location's credentials are stored in the `sync` section of `config.yaml` on the device, like every other key, and are never exported or synced. +- With encryption, the first device generates a random key, encrypts it with the sync passphrase and stores it in the sync location as `keyring`; every version and device record is encrypted with that key, in the age format. The sync location's provider sees only the file names (the version number and the first 8 characters of a device ID), the file sizes and their modification times. The authenticated encryption also guarantees that a version has not been tampered with: without the passphrase, no one can write a valid version. +- Without encryption, a version is a plain configuration pack. The provider of the sync location can read the configuration and the keys in it, and anyone who can write to the location can write a version and change the configuration. Choosing **Don't encrypt** when turning sync on shows this warning. To limit what tampering can do, a plugin added or with its script changed by sync starts disabled on this device and is turned on here, and an upstream whose address changed through sync is marked **Address changed by sync** once on its row. An unencrypted version carries a SHA-256 checksum, which only detects damage in transit. +- Turning encryption on later: once a sync passphrase is set, the latest version is uploaded again encrypted and the unencrypted versions are deleted; the other devices ask for the passphrase at their next sync. Turning it off needs the current passphrase and uploads the version again unencrypted. +- **Change sync passphrase…** re-encrypts only `keyring` and leaves the versions as they are. The other devices then show “The sync passphrase was changed on another device” and continue once the new passphrase is entered. +- If the sync passphrase is lost and no device is still syncing, the contents of the sync location cannot be decrypted; the only way forward is to stop syncing and turn sync on again. The configuration and backups on each device are not affected. + +## What is synced + +Everything in the configuration is synced, keys included, except local settings, ChatGPT sign-in tokens and data such as request records. The full list is under [Never exported or synced](#never-exported-or-synced). + +- **ChatGPT upstreams** appear disabled on a new device, marked **Sign-in required**. The sign-in tokens and the account ID that comes with a sign-in stay on each device and are never compared; signing in, renewing and signing out do not trigger a sync. Each device can sign in to a different ChatGPT account. +- **Upstreams not synced** are chosen in the sync settings under **Upstreams not synced**, a local setting. They are not uploaded, and an upstream of the same name from another device does not replace them, which suits an Ollama at a local address. Routes, aliases and groups that refer to them are not synced either and are listed in the sync settings under **Also not synced**. While sync is on, their rows carry a **Not synced** tag. +- **Plugins** are synced, and each stays turned on or off alike on every device; a plugin that may change tool calls runs only once it is approved on each device. The scripts of the plugins built into Core come from each device's Core and are upgraded with it, so they are not synced; whether they are on and their settings are. Different Core versions on the devices cause no sync because of their built-in plugins. +- **Environment variables** written as `${NAME}` are synced as written. On a device where the variable is not set, requests to that upstream fail there, the upstream row says why, and the rest of the configuration is unaffected. +- **Lite's general preferences**, the language, appearance, menu bar item, notifications and update checks, stay the same on every device, and a change made on another device takes effect at once. + +## Conflicts + +### How versions merge + +Each version records the version it was based on. A merge takes the last version both sides share as its base and compares the configuration on this device with the latest version in the sync location, unit by unit: + +| Item | Merge units | +|---|---| +| Upstream | Connection (service, address, protocol, authentication method, key, headers, outbound proxy, AWS settings); models (model list, the models it is limited to, models added by hand, specs); billing (billing mode, prices, price multiplier); other (concurrency limit, client identity forwarding, balance source, disabled state) | +| Proxy, gateway key, alias, group, route, price sheet, custom security rule | The whole item | +| Plugin | Script; settings; on or off | +| Route order | One unit | +| Global settings, Lite's general preferences | Each field | + +When two devices change different units of the same item, the changes merge by themselves: one device rotates an upstream's key while the other changes its prices, for instance. A conflict arises only when the same unit is changed differently on both sides, or deleted on one side and changed on the other. A rename counts as deleting the old name and adding the new one. A merged result must pass the configuration's checks, such as a route's upstream existing, and an item that fails them is treated as a conflict. + +When two devices upload at the same moment, the sync location holds two versions with the same number, and the next device to sync merges them into a new one. Each device keeps the version it last synced as the base for its next merge, so older versions deleted from the sync location while a device was offline do not affect the merge once it is back. + +### Resolving conflicts + +- A conflict does not stop syncing: everything else is merged and uploaded as usual, and each device keeps its own content for the conflicting items. +- The conflicts travel with the versions, so every device involved shows the same ones. Once they are resolved on any device, the result is uploaded as a new version and the others clear them; nothing is resolved twice. +- The **Sync** part and the top of the Upstreams page show a yellow notice, such as “2 sync conflicts”, which opens the **Sync conflicts** dialog. The notice can be ignored; the conflicts remain until they are resolved. +- The dialog lists each item with the time and device of each side's change and the fields that differ; clicking an item's name shows the difference. In the **Use** column, each item takes this device's version, the other device's, or **Keep both**: this device's content keeps the name, and the other side's is added as “Name (device name)”, which can be edited. Global settings, the route order and Lite's general preferences cannot keep both. **Later** closes the dialog without changes, and **Apply** writes the choices. + +## Sync states + +| State | Shown | What to do | +|---|---|---| +| Synced | “Synced · 2 minutes ago” in the settings; nothing on other pages | — | +| Syncing | “Syncing” in the settings | — | +| Conflict | A yellow notice such as “2 sync conflicts” | Resolve the conflicts | +| Sync location unavailable | After 2 hours of failures in a row, a yellow notice “Cannot reach the sync location” with the reason | Nothing: syncing resumes by itself, and the changes made here are uploaded then | +| Authentication failed | A yellow notice “The sync location denied access” | Correct the connection settings | +| Passphrase changed | A yellow notice “The sync passphrase was changed on another device” | Enter the new passphrase | +| Newer version | A yellow notice “Another device uses a newer version. Update ThinkWatch Lite to continue syncing.”; the device lists of the other devices show this one as “Paused: update needed” | Update ThinkWatch Lite | + +Nothing raises a system notification while sync runs normally. A state that needs attention raises one, following the notice setting in **Settings › General**, and every yellow notice can be ignored. + +When the latest version in the sync location was written by a newer Core that this device cannot read, the device pauses syncing and neither downloads nor uploads. Changes made in the meantime stay on the device and are merged by the usual rules after the update, so an older Core never overwrites fields a newer Core wrote. A remote core is updated on its server; the device list shows each device's Core version. + +A device record not updated for 180 days is removed automatically. Nothing syncs automatically in safe mode. + +## Stopping sync + +**Stop syncing…** offers two ways: + +- **Stop syncing**: the device keeps its current configuration, drops its sync settings and removes its device record from the sync location. The other devices are not affected. +- **Stop syncing and delete sync data**, after a further confirmation, deletes every version, every device record and `keyring` from the sync location. The other devices then report the location as empty and stop syncing. + +## Restore + +- **Restore…** on a version in the **Backups** list opens a preview of the differences between that version and the current configuration, item by item: added, removed or changed. Every item is checked by default, which replaces the current configuration with the version as a whole; an unchecked item keeps its current content. To bring back one deleted upstream, check only that one. +- Local settings keep their current values. A ChatGPT upstream of the same name keeps its current sign-in token rather than the older one in the backup, which a refresh may already have invalidated. +- A restore is itself recorded as a new version, so it can be undone. +- While sync is on, the restored configuration is uploaded as a new version and the other devices follow; the foot of the preview says so, for example “The restored configuration will sync to 2 other devices”. Upstreams not synced are not affected. + +### When the configuration cannot be read + +When the configuration cannot be read and the app runs in safe mode, the error page offers **Restore from backup…** next to **Repair**. It lists the versions on the device, and those in the sync location when it can be reached, and opens the restore preview for the chosen one. An upgrade does not rewrite the configuration and therefore makes no version of its own: a configuration that cannot be read after an upgrade is the latest version, and the content from before a repair is kept in the backups as well. + +## Connected clients + +Each client connected on this device has a gateway key written into its configuration file. When a sync, a restore or an import with **Overwrite** changes gateway keys, the following keeps those clients working: + +| Situation | What happens | +|---|---| +| The value of a gateway key a client uses is replaced | The configuration of those clients is rewritten, as when a key is rotated on the Keys page | +| A gateway key a client uses would be deleted | Sync: the key is kept and listed as a conflict, with a note such as “Used by 2 clients on this device”. Restore: the item is marked **Kept** in the preview and not deleted | +| **Replace this device's configuration with the synced data** when joining a sync | The keys clients on this device use are added to the synced data and then synced to the other devices; the joining steps say so | + +With a remote core, the Lite connected to it does the same when it is told of the change. + +## With a remote core + +- **Sync and backup** sits under the server's configuration in the settings, and backups, exports, imports and sync all apply to the configuration on the server. +- Exports and imports go through the control channel, and the configuration pack is saved and opened on the computer running Lite. +- A remote core can join a sync too. The server itself connects to the sync location, and a folder path is a path on the server, typed in by hand; the folder dialog is available only with the local core. +- Lite's general preferences stay on the computer running Lite and are not handed to the remote core: Lite adds them to an export and applies them on import, and they take no part in the remote core's backups or sync. + +## Command line + +On a server without a desktop, or to restore by hand when the configuration is broken, `twcore` has these commands: + +```sh +twcore export [--providers a,b] [--no-secrets] [--encrypt] -o FILE +twcore import FILE [--dry-run] [--on-conflict overwrite|rename|skip] +twcore backup list +twcore backup restore ID [--dry-run] +twcore sync status | now | enable | disable +``` + +Without `--on-conflict`, `import` asks about each conflict in the terminal; `--dry-run` previews without writing. `sync enable` asks for the sync location and passphrase interactively. On a server set up by the install script, the commands start with `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch`, like those in [Connecting to a remote core](/docs/lite/remote-core). + +## Never exported or synced + +| Content | Export | Sync | Backup on the device | Notes | +|---|---|---|---|---| +| Upstreams, proxies, aliases, groups, routes, the default route, price sheets, security rules, failover, auxiliary requests | Included | Included | Included | — | +| Plugin scripts and settings | Included | Included | Included | Imported disabled; for sync, see [What is synced](#what-is-synced) | +| Upstream keys, header values, AWS credentials, proxy passwords, gateway keys, keys from a Z.ai / BigModel sign-in | Included by default; can be turned off | Included | Included | `${NAME}` is kept as written, not expanded. When an export leaves keys out, the upstream keys are filled in after importing and the gateway keys are generated anew | +| ChatGPT sign-in tokens | Not included | Not included | Included | One refresh token used on two devices invalidates itself. The upstream itself is exported and synced, and each device signs in separately | +| Lite's general preferences: language, appearance, menu bar item, notifications, update checks | Included | Included | Included | With the local core, Core keeps them in the data directory | +| Local settings: listening, the control channel key, remote control, sync settings (including the upstreams not synced), log retention | Not included | Not included | Included | A restore keeps their current values | +| Client config locations, which clients are connected | Not included | Not included | Not included | These are paths on the device and the clients' own configuration files; on a new computer, clients are connected again on the Clients page | +| Remote connections | Not included | Not included | Not included | The address depends on the network the device is on; the connection key can be generated again on the server | +| Request records, cost records, session bodies | Not included | Not included | Not included | Data rather than configuration, and possibly gigabytes in size | + +## Related + +- [Import links](/docs/lite/import-links): links that share a single upstream, and their parameters. +- [Connecting to a remote core](/docs/lite/remote-core): managing ThinkWatch Core on a server from the app. +- [Features](/docs/lite/features): what each page does. diff --git a/src/content/docs-lite/zh-CN/architecture.md b/src/content/docs-lite/zh-CN/architecture.md index 54cc9bb..53a2e90 100644 --- a/src/content/docs-lite/zh-CN/architecture.md +++ b/src/content/docs-lite/zh-CN/architecture.md @@ -10,7 +10,7 @@ src-tauri/ Tauri 2 外壳:托管 core,渲染菜单栏与托盘 src-tauri/crates/ 客户端接管(tw-adopt)与配置扫描(tw-scan) ``` -- **`src-tauri/`** 为 Tauri 2 外壳,负责启动并托管 ThinkWatch Core:core 退出后自动重启;连续多次启动失败后进入安全模式,只运行控制面,配置、历史与回滚仍然可用。外壳在 macOS 上渲染菜单栏,在 Windows 上渲染通知区域的图标与菜单,在 Linux 上渲染系统托盘的图标与菜单,并负责发送系统通知。开机启动时启动的是应用本身,再由应用启动 core;core 不单独注册开机启动。 +- **`src-tauri/`** 为 Tauri 2 外壳,负责启动并托管 ThinkWatch Core:core 退出后自动重启;连续多次启动失败后进入安全模式,只运行控制面,配置与从备份恢复仍然可用。外壳在 macOS 上渲染菜单栏,在 Windows 上渲染通知区域的图标与菜单,在 Linux 上渲染系统托盘的图标与菜单,并负责发送系统通知。开机启动时启动的是应用本身,再由应用启动 core;core 不单独注册开机启动。 - **`src-tauri/crates/`** 包含 `tw-adopt` 与 `tw-scan`:前者把客户端指向网关并负责还原,后者为 MCP 页扫描客户端配置。两者都作用于运行应用的这台电脑,应用连接服务器上的 core 时也是如此。 - **`src/`** 为前端,使用 React 19 与 Tailwind 4 编写。 diff --git a/src/content/docs-lite/zh-CN/faq.md b/src/content/docs-lite/zh-CN/faq.md index 77e2ac7..9ce93a7 100644 --- a/src/content/docs-lite/zh-CN/faq.md +++ b/src/content/docs-lite/zh-CN/faq.md @@ -46,9 +46,9 @@ ThinkWatch Lite 只在客户端页接管客户端时修改它的配置,且只 ## ThinkWatch Lite 的数据保存在哪里?会连接哪些地址? -配置、密钥与请求记录都保存在本机:macOS 与 Linux 在 `~/.thinkwatch`,Windows 安装版在 `%APPDATA%\ThinkWatch`,Windows 绿色版在程序旁边的 `data\` 文件夹。网关默认只监听本机 `127.0.0.1` 的 8788 端口。 +配置、密钥与请求记录都保存在本机:macOS 与 Linux 在 `~/.thinkwatch`,Windows 安装版在 `%APPDATA%\ThinkWatch`,Windows 绿色版在程序旁边的 `data\` 文件夹。开启同步后,配置(含密钥)另有一份保存在用户选择的同步位置,缺省以同步口令加密。网关默认只监听本机 `127.0.0.1` 的 8788 端口。 -除了转发请求时连接所配置的上游,ThinkWatch Lite 还会连接 GitHub 检查更新,并每天更新一次 LiteLLM 公开的价目表。登录 ChatGPT 或 Z.ai 账号时会打开它们的授权页面;只有配置了远程 core 时才会连接远程 core。 +除了转发请求时连接所配置的上游,ThinkWatch Lite 还会连接 GitHub 检查更新,并每天更新一次 LiteLLM 公开的价目表。登录 ChatGPT 或 Z.ai 账号时会打开它们的授权页面;只有配置了远程 core 时才会连接远程 core,只有开启同步时才会连接所设的同步位置。 ## ThinkWatch Lite 会收集使用数据吗? diff --git a/src/content/docs-lite/zh-CN/features.md b/src/content/docs-lite/zh-CN/features.md index 6e1e2cb..b873c51 100644 --- a/src/content/docs-lite/zh-CN/features.md +++ b/src/content/docs-lite/zh-CN/features.md @@ -61,7 +61,7 @@ WSL 2 默认使用 NAT 网络,此时 Windows 上的网关无法从 WSL 内访 API 密钥和请求头的值可以写成 `${变量名}`,读取系统环境变量。macOS 上读的是登录 shell 里的环境变量,`~/.zshrc` 等文件中 `export` 的变量都会生效,Linux 同理(`~/.bashrc`、`~/.profile` 等);Windows 上读的是系统设置里配置的环境变量。修改变量后,重新打开应用即可生效。代理相关的变量(`HTTPS_PROXY` 等)和 `PATH` 不会被读取。 -中转站、服务商或 ThinkWatch 企业版控制台可以提供导入链接(`thinkwatch://import?…`,或网页形式 `https://thinkwat.ch/import#…`),打开预填好的「新建上游」对话框,服务类型、地址、密钥、请求头、并发上限、模型、模型规格与价目表都可以预先填写。对话框写明接收请求与密钥的主机;选择「下一步」或开始登录之前,不写入配置,也不发出任何请求。一条链接只能新增一个上游,不能设置代理、模型别名、路由或环境变量引用;需要更新版本应用的链接会提示更新。各服务类型的参数与链接生成器见[导入链接](/zh-CN/docs/lite/import-links)。 +中转站、服务商或 ThinkWatch 企业版控制台可以提供导入链接(`thinkwatch://import?…`,或网页形式 `https://thinkwat.ch/import#…`),打开预填好的「新建上游」对话框,服务类型、地址、密钥、请求头、并发上限、模型、模型规格与价目表都可以预先填写。对话框写明接收请求与密钥的主机;选择「下一步」或开始登录之前,不写入配置,也不发出任何请求。一条链接只能新增一个上游,不能设置代理、模型别名、路由或环境变量引用;需要更新版本应用的链接会提示更新。各服务类型的参数与链接生成器见[导入链接](/zh-CN/docs/lite/import-links)。上游行菜单中的「分享…」为已有的上游生成导入链接,可以选择是否包含密钥;多选上游之后,工具条上的「导出所选…」把它们导出为配置包(见[同步与备份](#同步与备份))。 ## 路由与故障转移 @@ -105,7 +105,13 @@ MCP 页管理客户端从自己的配置文件中加载的内容,这些内容 ## 设置 -设置页分为七节。「连接」列出本机 core 和已保存的远程 core,详见[连接远程 core](/zh-CN/docs/lite/remote-core)。「通用」设置语言、外观、菜单栏显示的内容(仅 macOS,也可设为不显示)、开机启动、提醒以系统通知发送、仅在应用内显示还是关闭,并可让设为不再显示的引导提示和忽略过的提醒重新显示。「网关监听」设置网关的访问范围(仅本机、所选网卡所在的局域网或所有网卡)、端口和放行网段。「故障转移」设置上游失败后暂停多久、请求交给下一个上游:连续失败几次后暂停、暂停多长,以及余额不足、额度用完和限流时各自的暂停时长;还设置无响应超时,即上游多久没有发出内容即被放弃(默认 300 秒,可设 30 到 3,600 秒),以及上游并发已满或密钥的分钟、小时上限用满时请求合计最多等待的时长(默认 30 秒)。「日志保留」分别设置请求报文与请求记录的保留天数,以及报文的空间上限;安全日志随请求记录一同过期。「关于」显示版本、检查更新,并可生成诊断包,其中的密钥与地址均已脱敏。「卸载」还原所有已接管的客户端并取消开机启动,应在删除应用之前执行。 +设置页分为八节。「连接」列出本机 core 和已保存的远程 core,详见[连接远程 core](/zh-CN/docs/lite/remote-core)。「通用」设置语言、外观、菜单栏显示的内容(仅 macOS,也可设为不显示)、开机启动、提醒以系统通知发送、仅在应用内显示还是关闭,并可让设为不再显示的引导提示和忽略过的提醒重新显示。「网关监听」设置网关的访问范围(仅本机、所选网卡所在的局域网或所有网卡)、端口和放行网段。「故障转移」设置上游失败后暂停多久、请求交给下一个上游:连续失败几次后暂停、暂停多长,以及余额不足、额度用完和限流时各自的暂停时长;还设置无响应超时,即上游多久没有发出内容即被放弃(默认 300 秒,可设 30 到 3,600 秒),以及上游并发已满或密钥的分钟、小时上限用满时请求合计最多等待的时长(默认 30 秒)。「日志保留」分别设置请求报文与请求记录的保留天数,以及报文的空间上限;安全日志随请求记录一同过期。「同步与备份」导出与导入配置、开启同步,并列出可以恢复的配置版本(见[同步与备份](#同步与备份))。「关于」显示版本、检查更新,并可生成诊断包,其中的密钥与地址均已脱敏。「卸载」还原所有已接管的客户端并取消开机启动,应在删除应用之前执行。 + +## 同步与备份 + +换电脑、重装或在多台设备上使用时,配置不必重新填写:可以导出为一个文件再导入,也可以经自选的文件夹、WebDAV 或 S3 兼容存储在各设备之间自动同步,同步内容缺省以口令加密。每次配置变更都留下一个可以恢复的版本,误删的上游也可以单独找回。 + +导出与导入的选项、各同步位置的配置方法、冲突与恢复见[同步与备份](/zh-CN/docs/lite/sync-and-backup)。 ## 菜单栏、系统托盘与通知 diff --git a/src/content/docs-lite/zh-CN/import-links.md b/src/content/docs-lite/zh-CN/import-links.md index 54011d4..09e78f9 100644 --- a/src/content/docs-lite/zh-CN/import-links.md +++ b/src/content/docs-lite/zh-CN/import-links.md @@ -2,7 +2,7 @@ ThinkWatch Lite 支持通过链接打开预填好设置的「新建上游」对话框。中转站、模型服务商或 ThinkWatch 企业版控制台可以把这样的链接放在控制台或开通通知中;用户打开链接后,在手动添加上游时所用的同一个对话框中核对设置,点几下即可创建上游。 -本页面向中转站与服务商的运营人员,说明链接的两种形式、按对话框分组的参数、各服务类型接受哪些参数、编码方式,以及链接刻意不能设置的内容。页面末尾附有链接生成器。 +本页面向中转站与服务商的运营人员,说明链接的两种形式、按对话框分组的参数、各服务类型接受哪些参数、编码方式,以及链接刻意不能设置的内容。页面末尾附有链接生成器。ThinkWatch Lite 自身也能为已有的上游生成这样的链接,见[由 Lite 生成](#由-lite-生成)。 `name`、`url`、`protocol`、`key`、`models` 以外的参数需要 ThinkWatch Lite 2026.10.16 或更高版本。只用这五个参数的链接在更早的版本中同样可以打开,为更早版本编写的链接全部继续有效。 @@ -213,6 +213,28 @@ thinkwatch://import?service=newapi&url={address}&key={key} 站点会填入自己的服务器地址和这一行的密钥,每把密钥的聊天菜单中随之出现这一项。打开它需要 ThinkWatch Lite 2026.10.16 或更新版本;版本更早或没有安装应用时,点击没有反应。 +## 由 Lite 生成 + +ThinkWatch Lite 也能为已有的上游生成导入链接,把一个上游交给另一台设备或另一位用户。上游行的 ⋯ 菜单中,「查看流量」之后是「分享…」,它打开「分享上游」对话框: + +- 对话框显示网页形式的链接,「复制网页链接」与「复制 thinkwatch:// 链接」分别复制两种形式。网页链接在未安装 Lite 的设备上显示安装说明,`#` 之后的内容不发送至网站服务器。 +- 「包含密钥」缺省开启,此时对话框提示「链接包含密钥。持有链接即可使用该密钥,请仅通过可信渠道发送。」关闭后链接不含密钥,接收方在对话框中自行填写。 +- 对话框底部列出链接包含与不包含的内容,例如「包含:地址、协议、密钥、2 个模型、余额来源。不包含:出站代理、路由。」 + +链接写入的内容如下: + +| 内容 | 是否写入 | 说明 | +|---|---|---| +| 服务类型、认证方式、站点、名称、地址、协议(`service`、`auth`、`site`、`name`、`url`、`protocol`) | 写入 | — | +| 并发上限、转发客户端身份、余额来源、模型列表、只用这些模型、手动规格、计费方式、价格倍率、价目(`concurrency`、`client_identity`、`balance`、`models`、`models_only`、`specs`、`billing`、`price_multiplier`、`prices`) | 写入 | 与缺省值相同的不写 | +| 密钥、请求头、AWS 凭据、OAuth 客户端凭据 | 由「包含密钥」决定,缺省写入 | 值写成 `${变量名}` 时不能写入,因为链接不接受这种写法;该项省略,并注明原因 | +| 出站代理、别名、路由、权重、停用状态、AWS profile | 不写入 | 导入链接不接受这些参数,见[链接不能设置的内容](#链接不能设置的内容) | +| ChatGPT、Z.ai / BigModel 的账号登录凭据 | 不写入 | 接收方停在登录一步,自行登录 | + +链接超过 8192 个字符时(通常因为价目或规格较多),链接形式不可用,对话框提示改用配置包。「导出为配置包…」打开导出对话框,并预先勾选这个上游;口令加密可选,缺省不加密。接收方通过「导入配置…」导入,配置包也能带上上游所用的出站代理等链接不能设置的内容,见[同步与备份](/zh-CN/docs/lite/sync-and-backup#导出配置)。 + +链接的生成与解析遵循本页的同一套规则:生成的链接在同一版本的应用中打开时,「新建上游」对话框中的设置与原上游一致,上表中不写入的内容除外。 + ## 链接中的密钥 含有密钥、Refresh Token 或私有访问密钥的链接本身就是凭据。应为每位客户单独生成链接,并通过私密渠道提供,例如客户自己的控制台。不含密钥的链接会让密钥栏位留空,由用户在对话框中填写。 diff --git a/src/content/docs-lite/zh-CN/overview.md b/src/content/docs-lite/zh-CN/overview.md index e3f780f..18ac253 100644 --- a/src/content/docs-lite/zh-CN/overview.md +++ b/src/content/docs-lite/zh-CN/overview.md @@ -13,6 +13,7 @@ ThinkWatch Lite 是 Claude Code、Codex 等 AI 客户端的本地网关,支持 - **每个请求都可追溯。** 命中的规则、每一次尝试、格式转换与费用都有记录,也可以重放到另一个上游对比;全部请求记录都可以搜索,包括请求与回答的内容。 - **路由与故障转移。** 按模型、工具、图片等条件分流;回答开始前上游出错时换用下一个,同一会话固定使用同一上游。 - **多种上游。** API 密钥、Amazon Bedrock、ChatGPT 与 Z.ai 账号、中转服务与本机模型,Anthropic、OpenAI、Gemini 接口之间自动转换。 +- **同步与备份。** 配置可以导出为一个文件,在新电脑上或重装之后导入,也可以经自选的云盘文件夹、WebDAV 或 S3 兼容存储在多台设备之间同步,缺省加密。每次配置变更都留下可以恢复的版本。 - **费用如实计算。** 估算的金额单独标注,无法计价的请求单独计数,不按零计入。 ## 页面 @@ -28,7 +29,7 @@ ThinkWatch Lite 是 Claude Code、Codex 等 AI 客户端的本地网关,支持 | 安全 | 安全日志,以及三项防护及其规则 | | MCP | 各客户端的 MCP 服务器、技能与钩子,以及配置扫描 | | 插件 | 改写请求与回答的 JavaScript 插件,及其权限、试运行与日志 | -| 设置 | 连接、语言、外观、菜单栏、提醒、网关监听、日志保留、更新与卸载 | +| 设置 | 连接、语言、外观、菜单栏、提醒、网关监听、日志保留、同步与备份、更新与卸载 | 各页的详细说明见[功能详解](/zh-CN/docs/lite/features)。 diff --git a/src/content/docs-lite/zh-CN/remote-core.md b/src/content/docs-lite/zh-CN/remote-core.md index 5b3524a..8ccb863 100644 --- a/src/content/docs-lite/zh-CN/remote-core.md +++ b/src/content/docs-lite/zh-CN/remote-core.md @@ -51,7 +51,7 @@ sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch twcore control-key - **概览、流量、密钥、上游、路由与安全**显示和修改的是服务器上 core 的配置与历史。 - **客户端与 MCP**仍作用于运行应用的这台电脑:客户端页把这台电脑上的客户端改为指向服务器的网关,MCP 页检查这台电脑上的 MCP 服务器、技能与钩子。两页顶部都有相应说明。其他机器上的客户端需要手动填写服务器的网关地址与网关密钥。 -- **设置**分为这台电脑上的应用(连接、通用、关于、卸载)与服务器上的配置(网关监听、日志保留)两部分。远程控制端口及其密钥只能在服务器上修改。 +- **设置**分为这台电脑上的应用(连接、通用、关于、卸载)与服务器上的配置(网关监听、故障转移、日志保留、同步与备份)两部分。服务器上的配置可以导出、导入与恢复,服务器也可以加入同步,见[同步与备份](/zh-CN/docs/lite/sync-and-backup#连接远程-core-时)。远程控制端口及其密钥只能在服务器上修改。 - **ChatGPT 登录**只能使用设备码,验证网址和设备码都可以复制:浏览器登录完成后会回到运行 core 的机器,也就是服务器。 - 上游密钥或请求头中的 `${变量名}` 读取的是服务器上 core 进程的环境变量,**系统代理**选项指的是服务器的系统代理。 - 不提供**诊断包**。经远程连接,服务器拒绝三项操作:生成诊断包、停止 core,以及修改 `listen.control`。 diff --git a/src/content/docs-lite/zh-CN/sync-and-backup.md b/src/content/docs-lite/zh-CN/sync-and-backup.md new file mode 100644 index 0000000..f3cd4d5 --- /dev/null +++ b/src/content/docs-lite/zh-CN/sync-and-backup.md @@ -0,0 +1,338 @@ +# 同步与备份 + +换电脑或重装之后,ThinkWatch Lite 的配置可以从一个文件整体导入,不必逐项重新填写。多台电脑与服务器上的 core 也可以经自选的同步位置共用一份配置:在一台设备上新增上游或更换密钥,其他设备随之更新。每次配置变更都会留下一个可以恢复的版本。 + +本页说明配置包、备份、导出与导入、开启同步及各种同步位置的配置方法、加密、冲突、恢复,以及不随导出与同步带走的内容。单个上游的分享见[导入链接](/zh-CN/docs/lite/import-links#由-lite-生成)。 + +这些功能集中在「设置 › 同步与备份」,分为「同步」与「备份」两部分。连接远程 core 时,这一节归在服务器的配置之下,管理的是服务器上的那份配置,见[连接远程 core 时](#连接远程-core-时)。 + +## 配置包 + +配置包是扩展名为 `.twconfig` 的单个文件,包含配置、插件脚本与 Lite 的通用偏好。导出、分享、本机的备份版本与同步位置上的版本都使用这种文件。 + +- 未加密的配置包是 ZIP 文件。以口令加密的配置包为 age 格式,口令经 scrypt 派生密钥,解密后是同样的 ZIP 文件。两者扩展名相同,由文件头区分。 +- 配置包不超过 16 MiB,只能包含约定的几类文件;出现其他路径、绝对路径或 `..` 时,整个配置包被拒绝。插件文件的 SHA-256 与配置中的记录不一致时,该插件标为错误。 +- 由较新版本导出、当前版本无法读取的配置包,提示「此配置包由较新版本导出,请先更新 ThinkWatch Lite」。较旧版本导出的配置包按当前版本读取,读取失败的字段在预览中列出,可以按「一键修复」的规则删除后再导入。 +- 口令错误时提示「口令不正确」。 + +## 备份 + +备份是配置的历史版本,存放在两处: + +| 存放位置 | 生成时机 | 保留 | +|---|---|---| +| 本机 | 每次配置变更生成一个版本:界面修改、命令行、手动编辑文件、同步、导入、恢复与一键修复。内容与上一版本相同时不生成,ChatGPT 令牌续期也不生成。 | 最近 10 个版本 | +| 同步位置 | 开启同步后,各设备上传的版本 | 最新与上一个版本 | + +本机版本是完整的配置包,包含插件、ChatGPT 登录令牌与本机设置,保存在数据目录的 `history/` 中。目录与文件只有所有者可读,不加密,与 `config.yaml` 本身的保护程度相同。这些版本取代了原先的修改记录,配置原文编辑器中的修改记录入口改为打开备份列表。 + +「备份」部分按时间倒序列出各个版本:时间、来源(如「界面修改」「导入」「手动编辑文件」「同步自 MacBook Air」)、存放位置(本机、同步位置)、内容(如「7 个上游,2 个插件」)与生成它的 Core 版本,正在使用的版本标为「当前」。同一版本同时存在于本机与同步位置时显示为一行,两处一并标出。其余版本各有「恢复…」与「另存为…」:前者见[恢复](#恢复),后者把该版本保存为配置包,可以选择以口令加密。 + +ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要另存一份时,使用「导出配置…」或某个版本的「另存为…」,文件可以存放在任意位置。 + +## 导出配置 + +导出有三个入口: + +- 「备份」部分的「导出配置…」,缺省导出全部配置; +- 上游页多选上游之后,工具条上的「导出所选…」; +- 上游分享对话框中的「导出为配置包…」。 + +三者打开同一个导出对话框;从上游页进入时,范围缺省为「选择项目」,并预先勾选对应的上游。 + +| 项目 | 说明 | +|---|---| +| 范围 | 「全部配置」或「选择项目」。选择项目时,按类别列出上游、出站代理、网关密钥、别名、分组、路由、价目表、安全规则、插件与全局设置。所选项目引用的其他项目自动勾选并标为「随附」,例如路由引用的上游、上游引用的出站代理。 | +| 包含密钥 | 缺省开启。包括上游密钥、请求头的值、AWS 凭据、代理密码、网关密钥,以及 Z.ai / BigModel 账号登录所得的密钥。写成 `${变量名}` 的值原样导出,不展开。 | +| 口令加密 | 缺省关闭。开启后须输入两次口令,至少 8 个字符。口令不保存,导入时须输入同一口令。 | +| 不随文件导出 | 列出这次被剔除的内容,例如「ChatGPT 登录(1 个上游),导入后需重新登录」、监听、远程控制与同步设置,以及请求记录。 | + +包含密钥且不加密时,对话框底部提示「文件包含明文密钥,请妥善保管。」 + +## 导入配置 + +导入有三个入口: + +- 「备份」部分的「导入配置…」; +- 把配置包拖放到主窗口; +- 新安装的应用(配置仍为初始配置)在上游页的空状态中,于「新建上游」旁提供「导入配置…」。 + +加密的配置包先要求输入口令。随后对话框写明配置包的概况:完整或部分配置、是否包含密钥、导出时间与导出它的 Core 版本;再按类别列出其中每一项及其状态: + +| 状态 | 条件 | 处理 | +|---|---|---| +| 新增 | 当前配置中没有同名项 | 直接导入;取消勾选即跳过 | +| 相同 | 同名且内容一致 | 不处理,只计入统计 | +| 冲突 | 同名而内容不同 | 必须选择「覆盖」「改名导入」或「跳过」,没有缺省值 | +| 冲突(全局设置) | 本机的该节已经修改,且与导入的值不同 | 必须选择「覆盖」或「跳过」 | +| 缺少密钥 | 配置包不含密钥 | 导入为停用状态,上游行显示「缺少密钥」标签,补填密钥后即可启用 | +| 需要登录 | ChatGPT 上游 | 导入为停用状态,上游行提供「登录…」 | +| 插件 | 插件脚本 | 导入为停用状态;可改写工具调用的插件须在启用时批准 | +| 错误 | 引用了本机不存在的 AWS profile、出站代理或上游;字段不合法;网关密钥的值与另一把现有密钥相同 | 不可导入,注明原因 | + +所有冲突都选定处理方式之前,「导入」不可用。列表上方的「全部覆盖」「全部改名导入」「全部跳过」一次处理全部冲突。 + +导入按以下规则进行: + +- 按名称逐项加入上游、出站代理、网关密钥、别名、分组、路由、价目表、自定义安全规则与插件。当前配置中有、配置包中没有的项保持不变,导入不删除任何内容。以一份配置整体替换当前配置属于恢复,见[恢复](#恢复)。 +- 全局设置(故障转移、各项防护的状态、默认路由、默认密钥、辅助请求、价目表设置)每一节作为一项:本机该节仍为缺省值时直接采用导入的值,否则与导入的值不同即构成冲突。 +- 「改名导入」的名称缺省为原名称加序号,如「公司中转 (2)」,可以在行内修改,但不能与现有名称重复。配置包中引用该项的路由、别名与分组随之改用新名称。 +- 「覆盖」以导入的内容替换同名项,本机引用该项的路由与别名不变。 +- 路由、别名或分组引用了被跳过的上游时,一并标为错误,并注明缺少的上游;改名导入的上游不在此列。 +- 配置包不含密钥时,导入的网关密钥重新生成。 +- 导入作为一个版本记入备份,因此可以恢复到导入之前的配置。预览之后配置又被修改时,导入会提示重新预览。 + +## 开启同步 + +同步让多台设备共用一份配置。设备指参与同步的一份 Core,可以是本机的 core,也可以是服务器上的远程 core。每台设备同一时间只连接一个同步位置。ThinkWatch 不提供官方托管的同步服务,同步位置由用户自行选择: + +| 类型 | 适用的存储 | 连接参数 | +|---|---|---| +| 文件夹 | 由 iCloud Drive、Dropbox、OneDrive、Google Drive、Syncthing 等同步的文件夹,或 NAS 的共享目录 | 文件夹路径 | +| WebDAV | 坚果云、Nextcloud、群晖 NAS、InfiniCloud 等 | 地址、用户名、密码(或应用密码) | +| S3 兼容存储 | Amazon S3、Cloudflare R2、阿里云 OSS、腾讯云 COS、MinIO 等 | 端点、区域、存储桶、前缀、Access Key ID、Secret Access Key | + +WebDAV 与 S3 兼容存储的请求可以经某个出站代理发出,缺省为直连。 + +未开启同步时,「同步」部分只有「开启同步…」与一句说明:「多台设备共用一份配置,并在同步位置保存最新与上一个版本。」开启同步分为三步: + +1. **同步位置。**选择类型并填写连接参数,「测试连接」检查能否读写。同步位置为空时,进入第 2 步设置加密;已有同步数据时,按其加密设置进入第 2 步输入同步口令,未加密时跳过第 2 步。 +2. **加密。**新建同步时选择加密(缺省)或不加密,加密时输入两次同步口令,至少 12 个字符。加入已有的同步时输入一次口令,并显示已在同步的设备及其最后同步时间。口令错误时提示「同步口令不正确」。 +3. **合并。**只在加入已有的同步、且本机已有配置时出现。按[导入](#导入配置)的规则逐项预览,冲突项逐项选择覆盖、改名或跳过;也可以选择「使用同步数据替换本机配置」。 + +本机配置仍为初始配置(新安装后未修改)时不出现第 3 步,直接使用同步数据。新安装的应用在上游页的空状态中提供「加入同步…」,直接打开上述步骤。加入之前的本机配置保留在备份中。本设备名称缺省为计算机名称,可以修改。 + +开启之后,「同步」部分显示状态(如「已同步 · 2 分钟前」)、同步位置及其连接参数、出站代理、加密、不同步的上游与本设备名称,以及参与同步的设备列表:每台设备的名称、最后同步时间、Core 版本与状态。「立即同步」立即检查一次。同步设置写入 `config.yaml`,修改后选择「保存」生效。 + +### 同步时机 + +| 时机 | 行为 | +|---|---| +| 本机配置变更 | 合并 10 秒内的变更,上传一个新版本。Core 退出前立即上传尚未上传的变更,上传失败的在下次启动时上传。 | +| 定时检查 | WebDAV 与 S3 兼容存储每 30 分钟检查一次是否有新版本,有则下载、合并并应用。文件夹监听文件变化,新版本到达后即时应用。 | +| 事件 | Core 启动、设备从睡眠中唤醒、网络恢复与打开主窗口时立即检查。 | +| 手动 | 「立即同步」。 | + +ChatGPT 令牌续期、本机设置的修改与默认插件的升级不上传。使用 WebDAV 或 S3 兼容存储时,其他设备的修改最迟 30 分钟内到达本设备,打开主窗口时立即到达;使用文件夹时,第三方客户端传送完成即到达。其他设备的版本经正常的配置写入流程应用,先校验,再热更新,并作为一个版本记入备份,来源记为「同步自」加设备名称。 + +## 各同步位置的配置方法 + +每台设备都要连接同一个同步位置:同一个云盘中的同一个文件夹、同一个 WebDAV 地址,或同一个存储桶与前缀。建议为 ThinkWatch 单独新建一个空文件夹或前缀,例如 `ThinkWatch`。 + +### iCloud Drive、Dropbox、OneDrive 等文件夹 + +选择「文件夹」,在文件夹选择对话框中选取云盘中为 ThinkWatch 新建的文件夹,每台设备选择同一个文件夹。文件在设备之间的传送由云盘客户端完成,ThinkWatch 只读写这个文件夹。 + +| 云盘 | 文件夹通常所在的位置 | +|---|---| +| iCloud Drive | macOS:访达边栏中的「iCloud 云盘」,即 `~/Library/Mobile Documents/com~apple~CloudDocs`;Windows:iCloud for Windows 的 `%USERPROFILE%\iCloudDrive` | +| Dropbox | macOS:`~/Library/CloudStorage/Dropbox`,较早的安装为 `~/Dropbox`;Windows:`%USERPROFILE%\Dropbox`;Linux:`~/Dropbox` | +| OneDrive | macOS:`~/Library/CloudStorage/` 下以 `OneDrive` 开头的文件夹;Windows:`%USERPROFILE%\OneDrive` | +| Google Drive | macOS:`~/Library/CloudStorage/` 下以 `GoogleDrive` 开头的文件夹;Windows:Google 云端硬盘盘符下的「我的云端硬盘」 | +| Syncthing、NAS 共享目录 | 各设备上同步或挂载的同一个文件夹 | + +云盘设为按需下载时,其他设备上传的版本在下载到本机之前按「尚未到达」处理,下载完成后再合并。把这个文件夹设为始终保留在本机(例如 OneDrive 的「始终保留在此设备上」),新版本即可随传送完成即时到达。 + +ThinkWatch 写入版本时先使用临时文件名,写完再改名,云盘因此不会传出写了一半的文件;无法解密或校验失败的文件同样视为尚未到达,不报错。 + +### 坚果云 + +1. 在坚果云中新建一个文件夹,例如 `ThinkWatch`。 +2. 在坚果云网页版的账户信息中打开「安全选项」,在第三方应用管理中添加一个应用,坚果云随即生成一个应用密码。 +3. 在 ThinkWatch Lite 中选择「WebDAV」:地址为 `https://dav.jianguoyun.com/dav/ThinkWatch/`,用户名为坚果云的登录账号,密码为第 2 步生成的应用密码,而不是登录密码。 + +### Nextcloud + +1. 在 Nextcloud 中新建一个文件夹,例如 `ThinkWatch`。 +2. 在个人设置的「安全」页中创建一个应用密码。开启了两步验证的账号必须使用应用密码。 +3. 地址为 `https://<服务器>/remote.php/dav/files/<用户名>/ThinkWatch/`,用户名为 Nextcloud 用户名,密码为第 2 步的应用密码。Nextcloud「文件」页的文件设置中也列出了 WebDAV 地址。 + +### 群晖 NAS + +1. 在 DSM 的套件中心安装 WebDAV Server,启用 HTTPS,缺省端口为 5006。 +2. 在一个共享文件夹中新建子文件夹,例如 `ThinkWatch`。建议为 ThinkWatch 单独建一个只能读写该文件夹的 DSM 账号。 +3. 地址为 `https://:5006/<共享文件夹>/ThinkWatch/`,用户名与密码为该 DSM 账号的用户名与密码。 + +ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校验。可以在 DSM 的「控制面板 › 安全性 › 证书」中为 NAS 的域名申请 Let's Encrypt 证书。只在局域网内使用时,也可以使用 `http://<内网 IP>:5005/…`:WebDAV 的 `http://` 只接受本机与内网地址,规则与[导入链接](/zh-CN/docs/lite/import-links#连接)相同,此时密码以及未加密时的配置以明文经过局域网。 + +### Cloudflare R2 + +1. 在 Cloudflare 控制台的 R2 中创建一个存储桶,不开启公开访问。 +2. 在 R2 的 API 令牌管理中创建一个令牌,权限为对象读写,并只适用于这个存储桶。创建后得到 Access Key ID 与 Secret Access Key。 +3. 在 ThinkWatch Lite 中选择「S3 兼容存储」: + +| 栏位 | 填写 | +|---|---| +| 端点 | `https://.r2.cloudflarestorage.com`,`` 为 Cloudflare 账户 ID;存储桶位于欧盟管辖区时为 `https://.eu.r2.cloudflarestorage.com` | +| 区域 | `auto` | +| 存储桶 | 第 1 步创建的存储桶名称 | +| 前缀 | 可以留空,也可以填写 `thinkwatch/` 等,与存储桶中的其他内容分开 | +| Access Key ID、Secret Access Key | 第 2 步得到的值 | + +### 阿里云 OSS + +| 栏位 | 填写 | +|---|---| +| 端点 | OSS 的 S3 兼容地址 `https://s3.oss-<地域 ID>.aliyuncs.com`,例如华东 1(杭州)为 `https://s3.oss-cn-hangzhou.aliyuncs.com` | +| 区域 | 地域 ID,例如 `cn-hangzhou` | +| 存储桶 | Bucket 名称 | +| Access Key ID、Secret Access Key | RAM 用户的 AccessKey ID 与 AccessKey Secret | + +建议在 RAM 访问控制中为 ThinkWatch 单独创建一个 RAM 用户,只授予这个 Bucket 的读写权限;Bucket 的读写权限保持为私有。 + +### 腾讯云 COS + +| 栏位 | 填写 | +|---|---| +| 端点 | `https://cos.<地域>.myqcloud.com`,例如广州为 `https://cos.ap-guangzhou.myqcloud.com` | +| 区域 | 地域简称,例如 `ap-guangzhou` | +| 存储桶 | 完整的存储桶名称,带有 APPID,形如 `<名称>-` | +| Access Key ID、Secret Access Key | 访问管理中 API 密钥的 SecretId 与 SecretKey | + +建议使用只拥有这个存储桶读写权限的子用户的密钥;存储桶的访问权限保持为私有读写。 + +### MinIO + +| 栏位 | 填写 | +|---|---| +| 端点 | MinIO 的 S3 API 地址,例如 `https://minio.example.com:9000`;缺省端口为 9000,不是控制台的端口 | +| 区域 | MinIO 设置的区域;未设置时为 `us-east-1` | +| 存储桶 | 事先创建的存储桶 | +| Access Key ID、Secret Access Key | 为 ThinkWatch 创建的访问密钥,其策略只允许读写这个存储桶 | + +### 其他 S3 兼容存储 + +端点为存储服务的 S3 API 地址,Amazon S3 为 `https://s3.<区域>.amazonaws.com`;区域为存储桶所在的区域。存储桶应设为私有,并使用只能访问这个存储桶的密钥。 + +## 加密 + +- 是否加密在开启同步时选择,缺省为加密,同一同步位置上的所有设备采用同一设置。每台设备加入时输入一次同步口令。口令与同步位置的凭据保存在本机 `config.yaml` 的 `sync` 节中,与其他密钥的保存方式相同,不随导出与同步带出。 +- 加密时,第一台设备生成一把随机密钥,以同步口令加密后作为 `keyring` 存放在同步位置;各个版本与设备记录都以这把密钥加密(age 格式)。同步位置的服务商只能看到文件名(版本序号与设备标识的前 8 位)、文件大小与修改时间。认证加密同时保证版本未被篡改,没有口令的人无法写入有效的版本。 +- 不加密时,版本是普通的配置包:同步位置的服务商可以读取配置及其中的密钥,任何对同步位置有写入权限的人都可以写入版本、改动配置。开启同步时选择「不加密」,会显示这一提示。为限制篡改的影响,不加密时经同步新增或脚本有变化的插件在本设备为停用状态,须在本设备上启用;上游地址经同步改变时,上游行以「地址经同步变更」标签提示一次。不加密的版本带有 SHA-256 校验和,只用于发现传输中的损坏。 +- 之后开启加密:设置同步口令后,最新版本重新以加密形式上传,未加密的版本随之删除,其他设备在下次同步时须输入口令。关闭加密须输入当前口令,随后重新上传未加密的版本。 +- 「更改同步口令…」只重新加密 `keyring`,已有版本不变;其他设备在下次同步时显示「同步口令已在其他设备上更改」,输入新口令后继续同步。 +- 同步口令遗失、且已没有设备仍在同步时,同步位置上的内容无法解密,只能停止同步后重新开启。各设备本机的配置与备份不受影响。 + +## 同步的内容 + +除本机设置、ChatGPT 登录令牌与请求记录等数据外,配置全部同步,包括密钥。完整的对照见[不导出、不同步的内容](#不导出不同步的内容)。 + +- **ChatGPT 上游**:在新设备上出现时为停用状态,上游行显示「需要登录」。登录令牌与登录所得的账号标识只保存在各自的设备上,不参与比较;登录、续期与退出都不触发同步。各设备可以登录不同的 ChatGPT 账号。 +- **不同步的上游**:在同步设置的「不同步的上游」中选择,属于本机设置。所选上游不上传,也不会被其他设备的同名上游覆盖,适用于地址在本机的 Ollama 等。引用这些上游的路由、别名与分组随之不同步,在同步设置中列于「随之不同步」之后。开启同步时,这些上游的行显示「不同步」标签。 +- **插件**:随同步分发,启用状态在各设备之间保持一致;可改写工具调用的插件须在每台设备上分别批准后才运行。Core 自带的默认插件,脚本由各设备的 Core 提供并随 Core 升级,不同步,启用状态与设置照常同步。各设备的 Core 版本不同时,默认插件的版本差异不引起同步。 +- **环境变量**:写成 `${变量名}` 的值原样同步。某台设备没有设置这个变量时,该上游在这台设备上的请求失败,上游行显示原因,不影响其他配置。 +- **Lite 的通用偏好**:语言、外观、菜单栏显示、通知与检查更新随同步保持一致,其他设备上的改动即时生效。 + +## 冲突 + +### 合并规则 + +每个版本都记录它所依据的上一版本。合并以两边共同的上一版本为基准,把本机当前的配置与同步位置上的最新版本按合并单位逐一比较: + +| 对象 | 合并单位 | +|---|---| +| 上游 | 连接(服务类型、地址、协议、认证方式、密钥、请求头、出站代理、AWS 设置);模型(模型列表、只用这些模型、手动添加的模型、规格);计费(计费方式、价目、价格倍率);其他(并发上限、转发客户端身份、余额来源、停用状态) | +| 出站代理、网关密钥、别名、分组、路由、价目表、自定义安全规则 | 整个对象 | +| 插件 | 脚本;设置;启用状态 | +| 路由顺序 | 一个单位 | +| 全局设置、Lite 的通用偏好 | 每个字段 | + +两台设备修改同一对象的不同合并单位时自动合并,例如一台更换了上游的密钥,另一台修改了它的价目。只有同一合并单位在两边被改为不同的内容,或一边删除而另一边修改时,才构成冲突。重命名按删除原名称、新增新名称处理。合并的结果须通过配置校验,例如路由引用的上游必须存在;校验不通过的对象按冲突处理。 + +两台设备同时上传时,同步位置上会出现序号相同的两个版本,下一台同步的设备把它们合并为一个新版本。每台设备在本机保存上次同步的版本,作为下一次合并的基准,因此设备离线期间同步位置上的旧版本被删除,不影响恢复连接后的合并。 + +### 处理冲突 + +- 冲突不阻断同步:其余内容照常合并与上传,冲突的对象在各设备上沿用各自的内容。 +- 冲突记录随版本上传,参与冲突的设备显示同一组冲突。在任一台设备上处理之后,结果作为新版本上传,其他设备随之清除这些冲突,不必重复处理。 +- 「同步」部分与上游页顶部显示黄色提示,例如「同步有 2 项冲突」,点击打开「同步冲突」对话框。提示可以忽略,冲突保留到处理为止。 +- 对话框逐项列出对象、双方的修改时间与设备名称以及差异的字段,点击对象名称查看差异。每一项在「采用」一列中选择本机、另一台设备,或「两者都保留」:本机的内容保留原名称,另一方的内容以「名称 (设备名称)」新增,名称可以修改。全局设置、路由顺序与 Lite 的通用偏好不提供「两者都保留」。「稍后处理」关闭对话框而不做改动,「应用」写入所选的结果。 + +## 同步状态 + +| 状态 | 显示 | 处理 | +|---|---|---| +| 已同步 | 设置中显示「已同步 · 2 分钟前」,其他页面不提示 | — | +| 同步中 | 设置中显示「正在同步」 | — | +| 冲突 | 黄色提示,如「同步有 2 项冲突」 | 处理冲突 | +| 同步位置不可用 | 连续 2 小时失败后,显示黄色提示「无法连接同步位置」及原因 | 恢复后自动继续,本机的修改随后上传 | +| 认证失败 | 黄色提示「同步位置拒绝访问」 | 修改连接参数 | +| 同步口令已更改 | 黄色提示「同步口令已在其他设备上更改」 | 输入新口令 | +| 版本过新 | 黄色提示「其他设备使用较新版本,更新 ThinkWatch Lite 后继续同步」;其他设备的设备列表中,这台设备显示「已暂停:需更新」 | 更新 ThinkWatch Lite | + +正常运转时不发送系统通知。需要处理的状态按「设置 › 通用」的提醒设置发送一次系统通知,黄色提示都可以忽略。 + +同步位置上的最新版本由较新的 Core 生成、本机无法读取时,本机暂停同步,既不下载也不上传。暂停期间本机的修改只保存在本机,更新之后再按合并规则合并,较旧的 Core 因此不会覆盖较新 Core 写入的字段。远程 core 需要在服务器上单独更新,设备列表显示每台设备的 Core 版本。 + +180 天未更新的设备记录自动移除。安全模式下不自动同步。 + +## 停止同步 + +「停止同步…」提供两种方式: + +- **停止同步**:本机保留当前配置,删除本机的同步设置,并从同步位置移除本设备的设备记录。其他设备不受影响。 +- **停止同步并删除同步数据**:须再次确认,删除同步位置上的全部版本、设备记录与 `keyring`。其他设备随后提示同步位置为空,并停止同步。 + +## 恢复 + +- 「备份」列表中,每个版本的「恢复…」打开恢复预览。预览按项目列出该版本与当前配置之间的差异(新增、删除、修改),缺省全部勾选,即以该版本整体替换当前配置;取消勾选的项目保持当前内容。只需找回一个已删除的上游时,只勾选这一项即可。 +- 本机设置保留当前的值。同名的 ChatGPT 上游保留当前的登录令牌,不写回备份中的旧令牌,因为旧令牌可能已在刷新后作废。 +- 恢复本身作为一个新版本记入备份,因此可以撤销。 +- 开启同步时,恢复的结果作为新版本上传,其他设备随之更新,预览对话框底部注明,例如「恢复后的配置将同步到其他 2 台设备」。不同步的上游不受影响。 + +### 配置无法读取时 + +配置无法读取而进入安全模式时,出错页在「一键修复」旁提供「从备份恢复…」:列出本机的版本,同步位置可用时也列出同步位置上的版本,选择后直接进入恢复预览。升级本身不改写配置,因此不单独生成版本;升级后无法读取的配置就是最新的版本,一键修复之前的内容同样保留在备份中。 + +## 与已接管客户端的关系 + +本机已接管的客户端在各自的配置文件中写有网关密钥。同步、恢复与导入(覆盖)改动网关密钥时按下表处理,客户端不会因此失效: + +| 情形 | 处理 | +|---|---| +| 客户端所用的网关密钥的值被替换 | 与在密钥页更换密钥时一样,自动改写这些客户端的配置 | +| 客户端所用的网关密钥将被删除 | 同步:保留这把密钥,作为冲突列出,并注明如「本机 2 个客户端正在使用」。恢复:预览中该项标为「保留」,不删除 | +| 加入同步时选择「使用同步数据替换本机配置」 | 本机客户端正在使用的密钥并入同步数据,随后同步到其他设备;加入的步骤中会注明 | + +连接远程 core 时,由连接着它的 Lite 在收到变更时做同样的处理。 + +## 连接远程 core 时 + +- 「同步与备份」归在设置中服务器的配置之下,备份、导出、导入与同步都作用于服务器上的配置。 +- 导出与导入经控制通道进行,配置包在运行 Lite 的这台电脑上保存与读取。 +- 远程 core 同样可以加入同步。同步位置的连接由服务器发起;文件夹路径是服务器上的路径,须手动填写,文件夹选择对话框只在连接本机 core 时可用。 +- Lite 的通用偏好保存在运行 Lite 的电脑上,不交给远程 core:导出时由 Lite 附加到配置包,导入时由 Lite 应用,不参与远程 core 的备份与同步。 + +## 命令行 + +在没有界面的服务器上,或在配置损坏时手动恢复,可以使用 `twcore` 的命令: + +```sh +twcore export [--providers a,b] [--no-secrets] [--encrypt] -o FILE +twcore import FILE [--dry-run] [--on-conflict overwrite|rename|skip] +twcore backup list +twcore backup restore ID [--dry-run] +twcore sync status | now | enable | disable +``` + +`import` 未指定 `--on-conflict` 时在终端中逐项选择,`--dry-run` 只预览、不写入。`sync enable` 以交互方式填写同步位置与口令。用安装脚本安装的服务器上,命令以 `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch` 开头,与[连接远程 core](/zh-CN/docs/lite/remote-core) 中的命令相同。 + +## 不导出、不同步的内容 + +| 内容 | 导出 | 同步 | 本机备份 | 说明 | +|---|---|---|---|---| +| 上游、出站代理、别名、分组、路由、默认路由、价目表、安全规则、故障转移、辅助请求 | 包含 | 包含 | 包含 | — | +| 插件脚本与设置 | 包含 | 包含 | 包含 | 导入后为停用状态;同步见[同步的内容](#同步的内容) | +| 上游密钥、请求头的值、AWS 凭据、代理密码、网关密钥、Z.ai / BigModel 登录所得的密钥 | 缺省包含,可以关闭 | 包含 | 包含 | `${变量名}` 原样保留,不展开。导出时关闭「包含密钥」的,导入时须补填上游密钥,网关密钥重新生成 | +| ChatGPT 登录令牌 | 不包含 | 不包含 | 包含 | 同一个刷新令牌在两台设备上使用会相互作废。上游本身照常导出与同步,每台设备分别登录 | +| Lite 的通用偏好:语言、外观、菜单栏显示、通知、检查更新 | 包含 | 包含 | 包含 | 连接本机 core 时由 Core 保存在数据目录中 | +| 本机设置:网关监听、控制通道密钥、远程控制、同步设置(含不同步的上游)、日志保留 | 不包含 | 不包含 | 包含 | 恢复时保留当前的值 | +| 客户端配置位置、客户端接管状态 | 不包含 | 不包含 | 不包含 | 这些是本机路径与各客户端自己的配置文件;换机后在客户端页重新接管 | +| 远程连接 | 不包含 | 不包含 | 不包含 | 连接地址与设备所在的网络有关;连接密钥可以在服务器上重新生成 | +| 请求记录、费用记录、会话正文 | 不包含 | 不包含 | 不包含 | 属于数据而不是配置,体积可达数 GB | + +## 相关文档 + +- [导入链接](/zh-CN/docs/lite/import-links):分享单个上游的链接及其参数。 +- [连接远程 core](/zh-CN/docs/lite/remote-core):让应用管理服务器上的 ThinkWatch Core。 +- [功能详解](/zh-CN/docs/lite/features):各页面的功能。 diff --git a/src/content/docs/_meta.ts b/src/content/docs/_meta.ts index 04108aa..4206077 100644 --- a/src/content/docs/_meta.ts +++ b/src/content/docs/_meta.ts @@ -159,8 +159,8 @@ export const products: Product[] = [ locales: both, group: "getStarted", summary: { - en: "Page by page: usage and cost, traffic, client setup, keys, upstreams, routing, the three protections, MCP, settings, the menu bar and notifications.", - "zh-CN": "逐页说明:用量与费用、流量、客户端接管、密钥、上游、路由、三项防护、MCP、设置、菜单栏与通知。", + en: "Page by page: usage and cost, traffic, client setup, keys, upstreams, routing, the three protections, MCP, settings, sync and backup, the menu bar and notifications.", + "zh-CN": "逐页说明:用量与费用、流量、客户端接管、密钥、上游、路由、三项防护、MCP、设置、同步与备份、菜单栏与通知。", }, }, { @@ -183,6 +183,16 @@ export const products: Product[] = [ "zh-CN": "让应用连接部署在服务器上的 ThinkWatch Core:服务器需要具备的条件、应用中的操作步骤、连接期间的变化,以及连接如何加密。", }, }, + { + slug: "sync-and-backup", + label: { en: "Sync and backup", "zh-CN": "同步与备份" }, + locales: both, + group: "getStarted", + summary: { + en: "Export and import the configuration, sync it across devices through a folder, WebDAV or S3-compatible storage, resolve conflicts, and restore from backups.", + "zh-CN": "导出与导入配置,经文件夹、WebDAV 或 S3 兼容存储在多台设备之间同步,处理冲突,以及从备份恢复。", + }, + }, { slug: "run-from-source", label: { en: "Build from source", "zh-CN": "从源码构建" }, @@ -318,8 +328,8 @@ export const products: Product[] = [ locales: both, group: "reference", summary: { - en: "For relays, vendors and ThinkWatch Enterprise: links that open the New upstream dialog filled in, the parameters each service takes, what the app checks, and a link builder.", - "zh-CN": "面向中转站、服务商与 ThinkWatch 企业版:打开预填好的「新建上游」对话框的链接、各服务类型接受的参数、应用所做的校验,以及链接生成器。", + en: "For relays, vendors and ThinkWatch Enterprise: links that open the New upstream dialog filled in, the parameters each service takes, what the app checks, the links the app writes for sharing, and a link builder.", + "zh-CN": "面向中转站、服务商与 ThinkWatch 企业版:打开预填好的「新建上游」对话框的链接、各服务类型接受的参数、应用所做的校验、应用分享上游时生成的链接,以及链接生成器。", }, }, { From 6ffbcd116b983203d172936b4818864fee0127b4 Mon Sep 17 00:00:00 2001 From: fylorn <249551762+fylorn@users.noreply.github.com> Date: Mon, 19 Oct 2026 11:22:29 +0800 Subject: [PATCH 2/3] Sync and backup docs: match the core implementation - The sync location's root is the folder, address or prefix itself; keyring, versions/ and devices/ sit directly in it. - S3 addressing is chosen from the endpoint: virtual-hosted for amazonaws.com, aliyuncs.com and myqcloud.com, path style otherwise. Plain http:// is accepted only for local and private addresses on both WebDAV and S3, with a MinIO example; requests go direct, through the system proxy or an outbound proxy, and do not follow redirects. - Upstreams with an OAuth refresh token keep their tokens on each device, like ChatGPT sign-ins: not exported or synced, kept on overwrite and restore. - Packs from older versions are repaired on import and the preview lists the repairs; packs from newer versions ask for an update. - The command-line section follows twcore's actual commands and options. Co-Authored-By: Claude Opus 5.5 --- src/content/docs-lite/en/sync-and-backup.md | 59 ++++++++++++------- .../docs-lite/zh-CN/sync-and-backup.md | 59 ++++++++++++------- 2 files changed, 76 insertions(+), 42 deletions(-) diff --git a/src/content/docs-lite/en/sync-and-backup.md b/src/content/docs-lite/en/sync-and-backup.md index 328273d..2246cb4 100644 --- a/src/content/docs-lite/en/sync-and-backup.md +++ b/src/content/docs-lite/en/sync-and-backup.md @@ -12,7 +12,7 @@ A configuration pack is a single file with the `.twconfig` extension that holds - An unencrypted pack is a ZIP file. A pack encrypted with a passphrase is in the age format, with the key derived from the passphrase by scrypt, and decrypts to the same ZIP file. Both have the same extension and are told apart by their header. - A pack is at most 16 MiB and may hold only the expected kinds of file; any other path, an absolute path or `..` rejects the whole pack. A plugin file whose SHA-256 does not match the configuration's record of it is marked as an error. -- A pack exported by a newer version that this version cannot read is reported with “This configuration pack was exported by a newer version. Update ThinkWatch Lite first.” A pack from an older version is read as the current version reads it; fields that fail are listed in the preview and can be removed by the rules of **Repair** before importing. +- A pack exported by a newer version asks for an update first: “This configuration pack was exported by a newer version. Update ThinkWatch Lite first.” A pack from an older version is repaired as it is imported: fields this version does not know are removed and invalid values go back to their defaults, and the preview lists each repair. - A wrong passphrase is reported as “Incorrect passphrase”. ## Backups @@ -24,7 +24,7 @@ Backups are earlier versions of the configuration, kept in two places: | This device | On every change to the configuration: in the app, from the command line, by editing the file by hand, by sync, import, restore or **Repair**. Not when the content equals the previous version, and not when a ChatGPT token is renewed. | The last 10 versions | | Sync location | The versions each device uploads once sync is on | The latest and the previous version | -A version on the device is a complete configuration pack, with the plugins, ChatGPT sign-in tokens and local settings, stored in `history/` in the data directory. The folder and files can be read only by their owner and are not encrypted, the same protection `config.yaml` itself has. These versions replace the former change history, and the history entry of the configuration file editor now opens the backup list. +A version on the device is a complete configuration pack, with the plugins, the sign-in tokens of ChatGPT and OAuth upstreams, and the local settings (the control channel key masked), stored in `history/` in the data directory. The folder and files can be read only by their owner and are not encrypted, the same protection `config.yaml` itself has. These versions replace the former change history, and the history entry of the configuration file editor now opens the backup list. The **Backups** part lists the versions newest first: the time, the source (such as **Changed in the app**, **Imported**, **File edited by hand** or **Synced from MacBook Air**), where it is stored (this device, the sync location), what it holds (such as “7 upstreams, 2 plugins”) and the Core version that made it; the version in use is marked **Current**. A version stored both on the device and in the sync location is one row with both places marked. Every other version has **Restore…**, described under [Restore](#restore), and **Save as…**, which saves it as a configuration pack, optionally encrypted with a passphrase. @@ -43,9 +43,9 @@ All three open the same export dialog. From the Upstreams page, the scope starts | Item | Description | |---|---| | Scope | **Whole configuration** or **Selected items**. Selected items lists the upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, security rules, plugins and global settings by category. Items that the selected ones refer to are checked automatically and marked **Dependency**, such as the upstream of a route or the proxy of an upstream. | -| Include keys | On by default. Covers upstream keys, header values, AWS credentials, proxy passwords, gateway keys and the keys obtained by signing in to a Z.ai / BigModel account. Values written as `${NAME}` are exported as written, not expanded. | +| Include keys | On by default. Covers upstream keys, header values, AWS credentials, OAuth client secrets, proxy passwords, gateway keys and the keys obtained by signing in to a Z.ai / BigModel account. Values written as `${NAME}` are exported as written, not expanded. | | Encrypt with passphrase | Off by default. When on, the passphrase is entered twice and has at least 8 characters. It is not stored, and importing asks for the same passphrase. | -| Not exported | What this export leaves out, such as “ChatGPT sign-in (1 upstream); sign in again after importing”, the listening, remote control and sync settings, and the request records. | +| Not exported | What this export leaves out: the sign-ins of ChatGPT accounts and of upstreams that use an OAuth refresh token (such as “ChatGPT sign-in (1 upstream); sign in again after importing”), the listening, remote control and sync settings, and the request records. | With keys included and no encryption, the foot of the dialog warns: “The file contains keys in plain text. Keep it safe.” @@ -65,8 +65,8 @@ An encrypted pack first asks for its passphrase. The dialog then states what the | Same | The same name with the same content | Nothing; counted in the summary | | Conflict | The same name with different content | **Overwrite**, **Import renamed** or **Skip** must be chosen; there is no default | | Conflict (global settings) | That section was changed on this device and differs from the pack | **Overwrite** or **Skip** must be chosen | -| Key missing | The pack holds no keys | Imported disabled, with a **Key missing** tag on the upstream row; it can be enabled once the key is filled in | -| Sign-in required | A ChatGPT upstream | Imported disabled, with **Sign in…** on the upstream row | +| Key missing | The pack holds no keys; or the upstream uses an OAuth refresh token and no upstream of that name here uses the same token endpoint | Imported disabled, with a **Key missing** tag on the upstream row; it can be enabled once the key, or for an OAuth upstream the refresh token, is filled in | +| Sign-in required | A ChatGPT upstream whose namesake here is not a ChatGPT account | Imported disabled, with **Sign in…** on the upstream row | | Plugin | A plugin script | Imported disabled; a plugin that may change tool calls is approved when it is turned on | | Error | It refers to an AWS profile, proxy or upstream that does not exist here; a field is invalid; or a gateway key has the value of another existing key | Cannot be imported, with the reason | @@ -77,7 +77,7 @@ An import follows these rules: - Upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, custom security rules and plugins are added one by one, by name. Items in the current configuration that the pack does not have stay as they are; an import never deletes anything. Replacing the whole configuration with another is a restore; see [Restore](#restore). - Each section of the global settings (failover, the state of each protection, the default route, the default key, auxiliary requests and the price sheet settings) is one item. When the section on this device is still at its defaults, the imported value is taken; otherwise a different imported value is a conflict. - **Import renamed** proposes the original name with a number, such as “Company relay (2)”, which can be edited in the row and must not be taken. Routes, aliases and groups in the pack that refer to the item use the new name. -- **Overwrite** replaces the item of that name with the imported one. Routes and aliases on this device that refer to it stay as they are. +- **Overwrite** replaces the item of that name with the imported one. Routes and aliases on this device that refer to it stay as they are. Overwriting a ChatGPT upstream here, or an OAuth upstream with the same token endpoint, keeps this device's sign-in tokens. - A route, alias or group that refers to a skipped upstream is marked as an error, naming the missing upstream. An upstream imported under a new name does not count as missing. - When the pack holds no keys, the imported gateway keys are generated anew. - An import is recorded as a version in the backups, so the configuration from before it can be restored. When the configuration changes after the preview, the import asks for a new preview. @@ -92,7 +92,7 @@ Sync lets several devices share one configuration. A device is one Core taking p | WebDAV | Jianguoyun (Nutstore), Nextcloud, Synology NAS, InfiniCloud and others | Address, user name and password (or app password) | | S3-compatible storage | Amazon S3, Cloudflare R2, Alibaba Cloud OSS, Tencent Cloud COS, MinIO and others | Endpoint, region, bucket, prefix, Access Key ID and Secret Access Key | -Requests to WebDAV and S3-compatible storage can go out through one of the outbound proxies; the default is direct. +Requests to WebDAV and S3-compatible storage go out directly (the default), through the system proxy or through one of the outbound proxies. For both, plain `http://` is accepted only for this computer and private networks, by the same rules as [import links](/docs/lite/import-links#connection); any other address needs `https://`, and its certificate is always verified. Redirects are not followed, so that the sync location's password is never carried to another host; the address entered must be the final one. While sync is off, the **Sync** part holds only **Turn on sync…** and one line: “Devices share one configuration, and the sync location keeps the latest and the previous version.” Turning sync on takes three steps: @@ -117,7 +117,7 @@ Renewing a ChatGPT token, changing local settings and upgrading a built-in plugi ## Setting up each sync location -Every device connects to the same location: the same folder in the same cloud drive, the same WebDAV address, or the same bucket and prefix. A new, empty folder or prefix for ThinkWatch, such as `ThinkWatch`, is recommended. +Every device connects to the same location: the same folder in the same cloud drive, the same WebDAV address, or the same bucket and prefix. The folder, address or prefix entered is itself the root of the sync location: `keyring` (when encrypted), `versions/` and `devices/` are placed directly in it, with no subfolder of their own. A new, empty folder or prefix for ThinkWatch, such as `ThinkWatch`, keeps them apart from other files. ### iCloud Drive, Dropbox, OneDrive and other folders @@ -153,7 +153,11 @@ ThinkWatch writes each version under a temporary name and renames it when it is 2. Create a subfolder in a shared folder, such as `ThinkWatch`. A DSM account of its own for ThinkWatch, with read and write access to that folder only, is recommended. 3. The address is `https://:5006//ThinkWatch/`, with that DSM account's user name and password. -ThinkWatch always verifies the TLS certificate, and the self-signed certificate DSM comes with does not pass. A Let's Encrypt certificate for the NAS's domain can be obtained in DSM under **Control Panel › Security › Certificate**. For use on the local network only, `http://:5005/…` works too: plain `http://` is accepted for WebDAV only on this computer and private networks, by the same rules as [import links](/docs/lite/import-links#connection), and the password, and the configuration when it is not encrypted, then cross the local network unencrypted. +ThinkWatch always verifies the TLS certificate, and the self-signed certificate DSM comes with does not pass. A Let's Encrypt certificate for the NAS's domain can be obtained in DSM under **Control Panel › Security › Certificate**. For use on the local network only, `http://:5005/…` works too; the password, and the configuration when it is not encrypted, then cross the local network unencrypted. + +### Addressing on S3-compatible storage + +The endpoint decides how the bucket is addressed, with nothing to set: when the endpoint's host ends in `amazonaws.com`, `aliyuncs.com` or `myqcloud.com`, that is Amazon S3, Alibaba Cloud OSS and Tencent Cloud COS, the bucket goes in the host name (virtual-hosted style); every other endpoint, such as Cloudflare R2, MinIO or an IP address, puts it in the path (path style). ### Cloudflare R2 @@ -195,7 +199,7 @@ The key of a sub-user allowed to read and write that bucket only is recommended, | Field | Value | |---|---| -| Endpoint | The S3 API address of MinIO, such as `https://minio.example.com:9000`; the default port is 9000, not the console's port | +| Endpoint | The S3 API address of MinIO, such as `https://minio.example.com:9000`, or `http://192.168.1.20:9000` for use on the local network only. The default port is 9000, not the console's port. MinIO is addressed in path style, so its buckets need no domain names | | Region | The region set in MinIO, or `us-east-1` when none is set | | Bucket | A bucket created beforehand | | Access Key ID, Secret Access Key | An access key created for ThinkWatch, with a policy that allows reading and writing that bucket only | @@ -215,9 +219,10 @@ The endpoint is the service's S3 API address, `https://s3..amazonaws.com ## What is synced -Everything in the configuration is synced, keys included, except local settings, ChatGPT sign-in tokens and data such as request records. The full list is under [Never exported or synced](#never-exported-or-synced). +Everything in the configuration is synced, keys included, except local settings, the sign-in tokens of ChatGPT and OAuth upstreams, and data such as request records. The full list is under [Never exported or synced](#never-exported-or-synced). - **ChatGPT upstreams** appear disabled on a new device, marked **Sign-in required**. The sign-in tokens and the account ID that comes with a sign-in stay on each device and are never compared; signing in, renewing and signing out do not trigger a sync. Each device can sign in to a different ChatGPT account. +- **Upstreams that use an OAuth refresh token** are signed in on each device, as ChatGPT upstreams are. The token endpoint, client ID and client secret are synced; the refresh and access tokens stay on each device, and refreshing them triggers no sync. A device that has no token yet needs its own refresh token filled in before it can use the upstream. Many OAuth services issue a new refresh token on every refresh and invalidate the old one, so one token shared by two devices would invalidate itself. - **Upstreams not synced** are chosen in the sync settings under **Upstreams not synced**, a local setting. They are not uploaded, and an upstream of the same name from another device does not replace them, which suits an Ollama at a local address. Routes, aliases and groups that refer to them are not synced either and are listed in the sync settings under **Also not synced**. While sync is on, their rows carry a **Not synced** tag. - **Plugins** are synced, and each stays turned on or off alike on every device; a plugin that may change tool calls runs only once it is approved on each device. The scripts of the plugins built into Core come from each device's Core and are upgraded with it, so they are not synced; whether they are on and their settings are. Different Core versions on the devices cause no sync because of their built-in plugins. - **Environment variables** written as `${NAME}` are synced as written. On a device where the variable is not set, requests to that upstream fail there, the upstream row says why, and the rest of the configuration is unaffected. @@ -276,13 +281,13 @@ A device record not updated for 180 days is removed automatically. Nothing syncs ## Restore - **Restore…** on a version in the **Backups** list opens a preview of the differences between that version and the current configuration, item by item: added, removed or changed. Every item is checked by default, which replaces the current configuration with the version as a whole; an unchecked item keeps its current content. To bring back one deleted upstream, check only that one. -- Local settings keep their current values. A ChatGPT upstream of the same name keeps its current sign-in token rather than the older one in the backup, which a refresh may already have invalidated. +- Local settings keep their current values. A ChatGPT upstream of the same name, and an OAuth upstream with the same token endpoint, keep their current sign-in tokens rather than the older ones in the backup, which a refresh may already have invalidated. - A restore is itself recorded as a new version, so it can be undone. - While sync is on, the restored configuration is uploaded as a new version and the other devices follow; the foot of the preview says so, for example “The restored configuration will sync to 2 other devices”. Upstreams not synced are not affected. ### When the configuration cannot be read -When the configuration cannot be read and the app runs in safe mode, the error page offers **Restore from backup…** next to **Repair**. It lists the versions on the device, and those in the sync location when it can be reached, and opens the restore preview for the chosen one. An upgrade does not rewrite the configuration and therefore makes no version of its own: a configuration that cannot be read after an upgrade is the latest version, and the content from before a repair is kept in the backups as well. +When the configuration cannot be read and the app runs in safe mode, the error page offers **Restore from backup…** next to **Repair**. It lists the versions on the device, and those in the sync location when it can be reached, and opens the restore preview for the chosen one. When the configuration cannot be read at all, a restore brings back the whole version, keeping only the current control channel key. An upgrade does not rewrite the configuration and therefore makes no version of its own: a configuration that cannot be read after an upgrade is the latest version, and the content from before a repair is kept in the backups as well. ## Connected clients @@ -308,14 +313,26 @@ With a remote core, the Lite connected to it does the same when it is told of th On a server without a desktop, or to restore by hand when the configuration is broken, `twcore` has these commands: ```sh -twcore export [--providers a,b] [--no-secrets] [--encrypt] -o FILE -twcore import FILE [--dry-run] [--on-conflict overwrite|rename|skip] +twcore export -o all.twconfig +twcore export --providers relay --no-secrets --encrypt -o relay.twconfig +twcore import all.twconfig --dry-run +twcore import all.twconfig --on-conflict rename twcore backup list -twcore backup restore ID [--dry-run] -twcore sync status | now | enable | disable +twcore backup restore --dry-run +twcore backup restore +twcore sync status +twcore sync now +twcore sync enable +twcore sync disable --purge ``` -Without `--on-conflict`, `import` asks about each conflict in the terminal; `--dry-run` previews without writing. `sync enable` asks for the sync location and passphrase interactively. On a server set up by the install script, the commands start with `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch`, like those in [Connecting to a remote core](/docs/lite/remote-core). +- `export` writes the whole configuration; `--providers` limits it to the comma-separated upstreams, with what they refer to. `--no-secrets` leaves the keys out, and `--encrypt` encrypts the file with a passphrase typed in the terminal, without echo, or taken from the `TWCORE_PASSPHRASE` environment variable. With neither, the file holds keys in plain text. +- `import` adds what is not here and asks about each item that differs; `--on-conflict overwrite|rename|skip` answers for all of them, and `--dry-run` shows what the import would do without writing. An encrypted pack asks for its passphrase or reads `TWCORE_PASSPHRASE` as well, and a pack from an older version lists its repairs first. +- `backup list` lists the versions newest first, with their ids; `backup restore` brings back a whole version, and `--dry-run` lists what would change. A restore keeps the local settings, the ChatGPT sign-ins and the gateway keys that clients on this machine use. +- `export`, `import` and `backup` work on the files in the data directory, so they work while core is not running, including when a broken configuration keeps it from starting; a running core picks their changes up within a second. +- `sync` talks to the running core over the control channel: `status` prints the state, the location and the devices, `now` syncs once, `enable` asks for the sync location and passphrase interactively, and `disable` stops syncing with this device keeping its configuration; `--purge` also deletes every version, device record and `keyring` from the sync location. + +On a server set up by the install script, the commands start with `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch`, like those in [Connecting to a remote core](/docs/lite/remote-core). `twcore --help` lists every option. ## Never exported or synced @@ -323,8 +340,8 @@ Without `--on-conflict`, `import` asks about each conflict in the terminal; `--d |---|---|---|---|---| | Upstreams, proxies, aliases, groups, routes, the default route, price sheets, security rules, failover, auxiliary requests | Included | Included | Included | — | | Plugin scripts and settings | Included | Included | Included | Imported disabled; for sync, see [What is synced](#what-is-synced) | -| Upstream keys, header values, AWS credentials, proxy passwords, gateway keys, keys from a Z.ai / BigModel sign-in | Included by default; can be turned off | Included | Included | `${NAME}` is kept as written, not expanded. When an export leaves keys out, the upstream keys are filled in after importing and the gateway keys are generated anew | -| ChatGPT sign-in tokens | Not included | Not included | Included | One refresh token used on two devices invalidates itself. The upstream itself is exported and synced, and each device signs in separately | +| Upstream keys, header values, AWS credentials, OAuth client secrets, proxy passwords, gateway keys, keys from a Z.ai / BigModel sign-in | Included by default; can be turned off | Included | Included | `${NAME}` is kept as written, not expanded. When an export leaves keys out, the upstream keys are filled in after importing and the gateway keys are generated anew | +| ChatGPT sign-in tokens; the refresh and access tokens of OAuth upstreams | Not included | Not included | Included | One refresh token used on two devices invalidates itself. The upstream itself is exported and synced, and each device signs in separately | | Lite's general preferences: language, appearance, menu bar item, notifications, update checks | Included | Included | Included | With the local core, Core keeps them in the data directory | | Local settings: listening, the control channel key, remote control, sync settings (including the upstreams not synced), log retention | Not included | Not included | Included | A restore keeps their current values | | Client config locations, which clients are connected | Not included | Not included | Not included | These are paths on the device and the clients' own configuration files; on a new computer, clients are connected again on the Clients page | diff --git a/src/content/docs-lite/zh-CN/sync-and-backup.md b/src/content/docs-lite/zh-CN/sync-and-backup.md index f3cd4d5..385af4c 100644 --- a/src/content/docs-lite/zh-CN/sync-and-backup.md +++ b/src/content/docs-lite/zh-CN/sync-and-backup.md @@ -12,7 +12,7 @@ - 未加密的配置包是 ZIP 文件。以口令加密的配置包为 age 格式,口令经 scrypt 派生密钥,解密后是同样的 ZIP 文件。两者扩展名相同,由文件头区分。 - 配置包不超过 16 MiB,只能包含约定的几类文件;出现其他路径、绝对路径或 `..` 时,整个配置包被拒绝。插件文件的 SHA-256 与配置中的记录不一致时,该插件标为错误。 -- 由较新版本导出、当前版本无法读取的配置包,提示「此配置包由较新版本导出,请先更新 ThinkWatch Lite」。较旧版本导出的配置包按当前版本读取,读取失败的字段在预览中列出,可以按「一键修复」的规则删除后再导入。 +- 由较新版本导出的配置包要求先更新,提示「此配置包由较新版本导出,请先更新 ThinkWatch Lite」。较旧版本导出的配置包在导入时自动修复:当前版本不认识的字段被删除,不合法的取值改回缺省值,预览中列出修改了哪几处。 - 口令错误时提示「口令不正确」。 ## 备份 @@ -24,7 +24,7 @@ | 本机 | 每次配置变更生成一个版本:界面修改、命令行、手动编辑文件、同步、导入、恢复与一键修复。内容与上一版本相同时不生成,ChatGPT 令牌续期也不生成。 | 最近 10 个版本 | | 同步位置 | 开启同步后,各设备上传的版本 | 最新与上一个版本 | -本机版本是完整的配置包,包含插件、ChatGPT 登录令牌与本机设置,保存在数据目录的 `history/` 中。目录与文件只有所有者可读,不加密,与 `config.yaml` 本身的保护程度相同。这些版本取代了原先的修改记录,配置原文编辑器中的修改记录入口改为打开备份列表。 +本机版本是完整的配置包,包含插件、ChatGPT 与 OAuth 上游的登录令牌,以及本机设置(其中的控制通道密钥以掩码保存),存放在数据目录的 `history/` 中。目录与文件只有所有者可读,不加密,与 `config.yaml` 本身的保护程度相同。这些版本取代了原先的修改记录,配置原文编辑器中的修改记录入口改为打开备份列表。 「备份」部分按时间倒序列出各个版本:时间、来源(如「界面修改」「导入」「手动编辑文件」「同步自 MacBook Air」)、存放位置(本机、同步位置)、内容(如「7 个上游,2 个插件」)与生成它的 Core 版本,正在使用的版本标为「当前」。同一版本同时存在于本机与同步位置时显示为一行,两处一并标出。其余版本各有「恢复…」与「另存为…」:前者见[恢复](#恢复),后者把该版本保存为配置包,可以选择以口令加密。 @@ -43,9 +43,9 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 | 项目 | 说明 | |---|---| | 范围 | 「全部配置」或「选择项目」。选择项目时,按类别列出上游、出站代理、网关密钥、别名、分组、路由、价目表、安全规则、插件与全局设置。所选项目引用的其他项目自动勾选并标为「随附」,例如路由引用的上游、上游引用的出站代理。 | -| 包含密钥 | 缺省开启。包括上游密钥、请求头的值、AWS 凭据、代理密码、网关密钥,以及 Z.ai / BigModel 账号登录所得的密钥。写成 `${变量名}` 的值原样导出,不展开。 | +| 包含密钥 | 缺省开启。包括上游密钥、请求头的值、AWS 凭据、OAuth 的 Client Secret、代理密码、网关密钥,以及 Z.ai / BigModel 账号登录所得的密钥。写成 `${变量名}` 的值原样导出,不展开。 | | 口令加密 | 缺省关闭。开启后须输入两次口令,至少 8 个字符。口令不保存,导入时须输入同一口令。 | -| 不随文件导出 | 列出这次被剔除的内容,例如「ChatGPT 登录(1 个上游),导入后需重新登录」、监听、远程控制与同步设置,以及请求记录。 | +| 不随文件导出 | 列出这次被剔除的内容:ChatGPT 账号与使用 OAuth 刷新令牌的上游的登录(例如「ChatGPT 登录(1 个上游),导入后需重新登录」)、监听、远程控制与同步设置,以及请求记录。 | 包含密钥且不加密时,对话框底部提示「文件包含明文密钥,请妥善保管。」 @@ -65,8 +65,8 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 | 相同 | 同名且内容一致 | 不处理,只计入统计 | | 冲突 | 同名而内容不同 | 必须选择「覆盖」「改名导入」或「跳过」,没有缺省值 | | 冲突(全局设置) | 本机的该节已经修改,且与导入的值不同 | 必须选择「覆盖」或「跳过」 | -| 缺少密钥 | 配置包不含密钥 | 导入为停用状态,上游行显示「缺少密钥」标签,补填密钥后即可启用 | -| 需要登录 | ChatGPT 上游 | 导入为停用状态,上游行提供「登录…」 | +| 缺少密钥 | 配置包不含密钥;或者是使用 OAuth 刷新令牌的上游,而本机没有令牌端点相同的同名上游 | 导入为停用状态,上游行显示「缺少密钥」标签,补填密钥(OAuth 上游为 Refresh Token)后即可启用 | +| 需要登录 | ChatGPT 上游,而本机的同名上游不是 ChatGPT 账号 | 导入为停用状态,上游行提供「登录…」 | | 插件 | 插件脚本 | 导入为停用状态;可改写工具调用的插件须在启用时批准 | | 错误 | 引用了本机不存在的 AWS profile、出站代理或上游;字段不合法;网关密钥的值与另一把现有密钥相同 | 不可导入,注明原因 | @@ -77,7 +77,7 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 - 按名称逐项加入上游、出站代理、网关密钥、别名、分组、路由、价目表、自定义安全规则与插件。当前配置中有、配置包中没有的项保持不变,导入不删除任何内容。以一份配置整体替换当前配置属于恢复,见[恢复](#恢复)。 - 全局设置(故障转移、各项防护的状态、默认路由、默认密钥、辅助请求、价目表设置)每一节作为一项:本机该节仍为缺省值时直接采用导入的值,否则与导入的值不同即构成冲突。 - 「改名导入」的名称缺省为原名称加序号,如「公司中转 (2)」,可以在行内修改,但不能与现有名称重复。配置包中引用该项的路由、别名与分组随之改用新名称。 -- 「覆盖」以导入的内容替换同名项,本机引用该项的路由与别名不变。 +- 「覆盖」以导入的内容替换同名项,本机引用该项的路由与别名不变。覆盖本机同名的 ChatGPT 上游,或令牌端点相同的 OAuth 上游时,保留本机的登录令牌。 - 路由、别名或分组引用了被跳过的上游时,一并标为错误,并注明缺少的上游;改名导入的上游不在此列。 - 配置包不含密钥时,导入的网关密钥重新生成。 - 导入作为一个版本记入备份,因此可以恢复到导入之前的配置。预览之后配置又被修改时,导入会提示重新预览。 @@ -92,7 +92,7 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 | WebDAV | 坚果云、Nextcloud、群晖 NAS、InfiniCloud 等 | 地址、用户名、密码(或应用密码) | | S3 兼容存储 | Amazon S3、Cloudflare R2、阿里云 OSS、腾讯云 COS、MinIO 等 | 端点、区域、存储桶、前缀、Access Key ID、Secret Access Key | -WebDAV 与 S3 兼容存储的请求可以经某个出站代理发出,缺省为直连。 +WebDAV 与 S3 兼容存储的请求可以直连(缺省)、经系统代理,或经某个出站代理发出。两者的 `http://` 地址都只接受本机与内网地址,规则与[导入链接](/zh-CN/docs/lite/import-links#连接)相同;其他地址须使用 `https://`,证书一律校验。请求不跟随重定向,以免同步位置的密码被带到别的主机,因此须填写最终的地址。 未开启同步时,「同步」部分只有「开启同步…」与一句说明:「多台设备共用一份配置,并在同步位置保存最新与上一个版本。」开启同步分为三步: @@ -117,7 +117,7 @@ ChatGPT 令牌续期、本机设置的修改与默认插件的升级不上传。 ## 各同步位置的配置方法 -每台设备都要连接同一个同步位置:同一个云盘中的同一个文件夹、同一个 WebDAV 地址,或同一个存储桶与前缀。建议为 ThinkWatch 单独新建一个空文件夹或前缀,例如 `ThinkWatch`。 +每台设备都要连接同一个同步位置:同一个云盘中的同一个文件夹、同一个 WebDAV 地址,或同一个存储桶与前缀。填写的文件夹、地址或前缀本身就是同步位置的根,`keyring`(仅加密时)、`versions/` 与 `devices/` 直接存放在其中,不另建子文件夹。因此应为 ThinkWatch 单独新建一个空文件夹或前缀,例如 `ThinkWatch`,不与其他文件混放。 ### iCloud Drive、Dropbox、OneDrive 等文件夹 @@ -153,7 +153,11 @@ ThinkWatch 写入版本时先使用临时文件名,写完再改名,云盘因 2. 在一个共享文件夹中新建子文件夹,例如 `ThinkWatch`。建议为 ThinkWatch 单独建一个只能读写该文件夹的 DSM 账号。 3. 地址为 `https://:5006/<共享文件夹>/ThinkWatch/`,用户名与密码为该 DSM 账号的用户名与密码。 -ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校验。可以在 DSM 的「控制面板 › 安全性 › 证书」中为 NAS 的域名申请 Let's Encrypt 证书。只在局域网内使用时,也可以使用 `http://<内网 IP>:5005/…`:WebDAV 的 `http://` 只接受本机与内网地址,规则与[导入链接](/zh-CN/docs/lite/import-links#连接)相同,此时密码以及未加密时的配置以明文经过局域网。 +ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校验。可以在 DSM 的「控制面板 › 安全性 › 证书」中为 NAS 的域名申请 Let's Encrypt 证书。只在局域网内使用时,也可以使用 `http://<内网 IP>:5005/…`,此时密码以及未加密时的配置以明文经过局域网。 + +### S3 兼容存储的寻址方式 + +寻址方式由端点自动决定,无需设置:端点的主机以 `amazonaws.com`、`aliyuncs.com` 或 `myqcloud.com` 结尾时,存储桶写在主机名中(虚拟主机方式),即 Amazon S3、阿里云 OSS 与腾讯云 COS;其他端点,例如 Cloudflare R2、MinIO 或一个 IP 地址,存储桶写在路径中(路径方式)。 ### Cloudflare R2 @@ -195,7 +199,7 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 | 栏位 | 填写 | |---|---| -| 端点 | MinIO 的 S3 API 地址,例如 `https://minio.example.com:9000`;缺省端口为 9000,不是控制台的端口 | +| 端点 | MinIO 的 S3 API 地址,例如 `https://minio.example.com:9000`;只在局域网内使用时也可以写 `http://192.168.1.20:9000`。缺省端口为 9000,不是控制台的端口。MinIO 使用路径方式,无需为存储桶配置域名 | | 区域 | MinIO 设置的区域;未设置时为 `us-east-1` | | 存储桶 | 事先创建的存储桶 | | Access Key ID、Secret Access Key | 为 ThinkWatch 创建的访问密钥,其策略只允许读写这个存储桶 | @@ -215,9 +219,10 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 ## 同步的内容 -除本机设置、ChatGPT 登录令牌与请求记录等数据外,配置全部同步,包括密钥。完整的对照见[不导出、不同步的内容](#不导出不同步的内容)。 +除本机设置、ChatGPT 与 OAuth 上游的登录令牌,以及请求记录等数据外,配置全部同步,包括密钥。完整的对照见[不导出、不同步的内容](#不导出不同步的内容)。 - **ChatGPT 上游**:在新设备上出现时为停用状态,上游行显示「需要登录」。登录令牌与登录所得的账号标识只保存在各自的设备上,不参与比较;登录、续期与退出都不触发同步。各设备可以登录不同的 ChatGPT 账号。 +- **使用 OAuth 刷新令牌的上游**:与 ChatGPT 上游一样,每台设备分别登录。令牌端点、Client ID 与 Client Secret 照常同步;Refresh Token 与 Access Token 只保存在各自的设备上,令牌刷新不触发同步。在还没有令牌的设备上,须填入这台设备自己的 Refresh Token 后才能使用。这是因为许多 OAuth 服务每次刷新都换发新的 Refresh Token 并作废旧的,同一个令牌在两台设备上使用会相互作废。 - **不同步的上游**:在同步设置的「不同步的上游」中选择,属于本机设置。所选上游不上传,也不会被其他设备的同名上游覆盖,适用于地址在本机的 Ollama 等。引用这些上游的路由、别名与分组随之不同步,在同步设置中列于「随之不同步」之后。开启同步时,这些上游的行显示「不同步」标签。 - **插件**:随同步分发,启用状态在各设备之间保持一致;可改写工具调用的插件须在每台设备上分别批准后才运行。Core 自带的默认插件,脚本由各设备的 Core 提供并随 Core 升级,不同步,启用状态与设置照常同步。各设备的 Core 版本不同时,默认插件的版本差异不引起同步。 - **环境变量**:写成 `${变量名}` 的值原样同步。某台设备没有设置这个变量时,该上游在这台设备上的请求失败,上游行显示原因,不影响其他配置。 @@ -276,13 +281,13 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 ## 恢复 - 「备份」列表中,每个版本的「恢复…」打开恢复预览。预览按项目列出该版本与当前配置之间的差异(新增、删除、修改),缺省全部勾选,即以该版本整体替换当前配置;取消勾选的项目保持当前内容。只需找回一个已删除的上游时,只勾选这一项即可。 -- 本机设置保留当前的值。同名的 ChatGPT 上游保留当前的登录令牌,不写回备份中的旧令牌,因为旧令牌可能已在刷新后作废。 +- 本机设置保留当前的值。同名的 ChatGPT 上游,以及令牌端点相同的 OAuth 上游,保留当前的登录令牌,不写回备份中的旧令牌,因为旧令牌可能已在刷新后作废。 - 恢复本身作为一个新版本记入备份,因此可以撤销。 - 开启同步时,恢复的结果作为新版本上传,其他设备随之更新,预览对话框底部注明,例如「恢复后的配置将同步到其他 2 台设备」。不同步的上游不受影响。 ### 配置无法读取时 -配置无法读取而进入安全模式时,出错页在「一键修复」旁提供「从备份恢复…」:列出本机的版本,同步位置可用时也列出同步位置上的版本,选择后直接进入恢复预览。升级本身不改写配置,因此不单独生成版本;升级后无法读取的配置就是最新的版本,一键修复之前的内容同样保留在备份中。 +配置无法读取而进入安全模式时,出错页在「一键修复」旁提供「从备份恢复…」:列出本机的版本,同步位置可用时也列出同步位置上的版本,选择后直接进入恢复预览。配置完全无法读取时,恢复取回整个版本,只保留当前的控制通道密钥。升级本身不改写配置,因此不单独生成版本;升级后无法读取的配置就是最新的版本,一键修复之前的内容同样保留在备份中。 ## 与已接管客户端的关系 @@ -308,14 +313,26 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 在没有界面的服务器上,或在配置损坏时手动恢复,可以使用 `twcore` 的命令: ```sh -twcore export [--providers a,b] [--no-secrets] [--encrypt] -o FILE -twcore import FILE [--dry-run] [--on-conflict overwrite|rename|skip] +twcore export -o all.twconfig +twcore export --providers relay --no-secrets --encrypt -o relay.twconfig +twcore import all.twconfig --dry-run +twcore import all.twconfig --on-conflict rename twcore backup list -twcore backup restore ID [--dry-run] -twcore sync status | now | enable | disable +twcore backup restore <编号> --dry-run +twcore backup restore <编号> +twcore sync status +twcore sync now +twcore sync enable +twcore sync disable --purge ``` -`import` 未指定 `--on-conflict` 时在终端中逐项选择,`--dry-run` 只预览、不写入。`sync enable` 以交互方式填写同步位置与口令。用安装脚本安装的服务器上,命令以 `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch` 开头,与[连接远程 core](/zh-CN/docs/lite/remote-core) 中的命令相同。 +- `export` 导出全部配置;`--providers` 只导出以逗号分隔的这些上游,连同它们引用的项目。`--no-secrets` 不含密钥,`--encrypt` 以口令加密,口令在终端中输入(不回显),或取自环境变量 `TWCORE_PASSPHRASE`。两者都不加时,文件含明文密钥。 +- `import` 加入本机没有的项目,与本机不同的项目逐项询问;`--on-conflict overwrite|rename|skip` 对所有这样的项目一并作答,`--dry-run` 只显示导入会做什么,不写入。加密的配置包同样询问口令或读取 `TWCORE_PASSPHRASE`;较旧版本导出的配置包先列出自动修复之处。 +- `backup list` 按时间倒序列出版本及其编号;`backup restore` 恢复整个版本,`--dry-run` 只列出会改变的内容。恢复保留本机设置、ChatGPT 登录与本机客户端正在使用的网关密钥。 +- `export`、`import` 与 `backup` 直接读写数据目录中的文件,core 未运行时同样可用,配置损坏、core 无法启动时也能恢复;正在运行的 core 在一秒内读入改动。 +- `sync` 经控制通道与正在运行的 core 通信:`status` 显示状态、同步位置与设备,`now` 立即同步一次,`enable` 以交互方式填写同步位置与口令,`disable` 停止同步、本机保留当前配置,加 `--purge` 同时删除同步位置上的全部版本、设备记录与 `keyring`。 + +用安装脚本安装的服务器上,命令以 `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch` 开头,与[连接远程 core](/zh-CN/docs/lite/remote-core) 中的命令相同。各命令的完整参数见 `twcore <命令> --help`。 ## 不导出、不同步的内容 @@ -323,8 +340,8 @@ twcore sync status | now | enable | disable |---|---|---|---|---| | 上游、出站代理、别名、分组、路由、默认路由、价目表、安全规则、故障转移、辅助请求 | 包含 | 包含 | 包含 | — | | 插件脚本与设置 | 包含 | 包含 | 包含 | 导入后为停用状态;同步见[同步的内容](#同步的内容) | -| 上游密钥、请求头的值、AWS 凭据、代理密码、网关密钥、Z.ai / BigModel 登录所得的密钥 | 缺省包含,可以关闭 | 包含 | 包含 | `${变量名}` 原样保留,不展开。导出时关闭「包含密钥」的,导入时须补填上游密钥,网关密钥重新生成 | -| ChatGPT 登录令牌 | 不包含 | 不包含 | 包含 | 同一个刷新令牌在两台设备上使用会相互作废。上游本身照常导出与同步,每台设备分别登录 | +| 上游密钥、请求头的值、AWS 凭据、OAuth 的 Client Secret、代理密码、网关密钥、Z.ai / BigModel 登录所得的密钥 | 缺省包含,可以关闭 | 包含 | 包含 | `${变量名}` 原样保留,不展开。导出时关闭「包含密钥」的,导入时须补填上游密钥,网关密钥重新生成 | +| ChatGPT 登录令牌;OAuth 上游的 Refresh Token 与 Access Token | 不包含 | 不包含 | 包含 | 同一个刷新令牌在两台设备上使用会相互作废。上游本身照常导出与同步,每台设备分别登录 | | Lite 的通用偏好:语言、外观、菜单栏显示、通知、检查更新 | 包含 | 包含 | 包含 | 连接本机 core 时由 Core 保存在数据目录中 | | 本机设置:网关监听、控制通道密钥、远程控制、同步设置(含不同步的上游)、日志保留 | 不包含 | 不包含 | 包含 | 恢复时保留当前的值 | | 客户端配置位置、客户端接管状态 | 不包含 | 不包含 | 不包含 | 这些是本机路径与各客户端自己的配置文件;换机后在客户端页重新接管 | From 3d013bb18e2e89094e05284599353780814734a3 Mon Sep 17 00:00:00 2001 From: fylorn <249551762+fylorn@users.noreply.github.com> Date: Mon, 19 Oct 2026 21:18:51 +0800 Subject: [PATCH 3/3] Sync and backup docs: final core behavior and Lite wording - Needs ThinkWatch Lite 2026.10.25 (core 0.78.0 on a remote server). - An encrypted location never takes plain versions or turns encryption off; a missing or older keyring, an emptied location and unusable sync settings pause syncing and offer "Upload this device's configuration" or "Stop syncing", never stopping by themselves. Turning encryption off keeps each device's passphrase. - Joining takes the sync data's default gateway key and updates the clients connected on this device. - Importing a pack without keys reuses this device's key only for the same destination; Rename never reuses keys, passwords or sign-ins. - Upstream addresses with a backslash or credentials before the host are rejected; such a configuration opens in safe mode. - Oversized general preferences are skipped in sync; partial restores keep or bring along referenced items; upstream, route and plugin order merge as units; local http:// locations go direct; bucket and prefix rules; twcore sync republish. - UI labels in both languages follow the app's own strings, including config pack, Rename, Entire config, Included, Change passphrase and the Chinese name for groups. Co-Authored-By: Claude Opus 5.5 --- src/content/docs-lite/en/features.md | 4 +- src/content/docs-lite/en/import-links.md | 11 +- src/content/docs-lite/en/sync-and-backup.md | 150 ++++++++++-------- src/content/docs-lite/zh-CN/features.md | 4 +- src/content/docs-lite/zh-CN/import-links.md | 9 +- .../docs-lite/zh-CN/sync-and-backup.md | 80 ++++++---- 6 files changed, 146 insertions(+), 112 deletions(-) diff --git a/src/content/docs-lite/en/features.md b/src/content/docs-lite/en/features.md index 3129349..dac62dd 100644 --- a/src/content/docs-lite/en/features.md +++ b/src/content/docs-lite/en/features.md @@ -61,7 +61,7 @@ Each upstream is also compared over the last 7 days with the others serving the API keys and header values can be written as `${NAME}` to read a system environment variable. On macOS these come from the login shell, so variables exported in `~/.zshrc` and similar files apply, and the same holds on Linux (`~/.bashrc`, `~/.profile` and so on); on Windows they are the environment variables configured in system settings. After a variable changes, reopening the app picks it up. Proxy variables such as `HTTPS_PROXY`, and `PATH`, are not read. -A relay, a vendor or a ThinkWatch Enterprise console can hand out an import link, `thinkwatch://import?…` or its web form `https://thinkwat.ch/import#…`, that opens the New upstream dialog with the service, address, key, headers, concurrency limit, models, model specs and a price sheet already filled in. The dialog names the host that will receive requests and keys, and nothing is written or sent before Next is chosen or a sign-in is started. A link only ever adds one upstream and cannot set proxies, aliases, routes or environment variable references; a link that needs a newer version of the app says so. The parameters for each service and a link builder are in [Import links](/docs/lite/import-links). **Share…** in an upstream row's menu writes an import link for an existing upstream, with or without its key, and **Export selected…** on the toolbar exports the selected upstreams as a configuration pack (see [Sync and backup](#sync-and-backup)). +A relay, a vendor or a ThinkWatch Enterprise console can hand out an import link, `thinkwatch://import?…` or its web form `https://thinkwat.ch/import#…`, that opens the New upstream dialog with the service, address, key, headers, concurrency limit, models, model specs and a price sheet already filled in. The dialog names the host that will receive requests and keys, and nothing is written or sent before Next is chosen or a sign-in is started. A link only ever adds one upstream and cannot set proxies, aliases, routes or environment variable references; a link that needs a newer version of the app says so. The parameters for each service and a link builder are in [Import links](/docs/lite/import-links). **Share…** in an upstream row's menu writes an import link for an existing upstream, with or without its key, and **Export N selected upstreams…** on the toolbar exports the selected upstreams as a config pack (see [Sync and backup](#sync-and-backup)). ## Routing and failover @@ -111,7 +111,7 @@ Settings has eight sections. Connection lists the local core and the saved remot A new computer, a reinstall or a second device does not mean entering the configuration again: it can be exported to one file and imported, or synced between devices automatically through a folder, WebDAV or S3-compatible storage of the user's choosing, encrypted with a passphrase by default. Every change to the configuration leaves a version that can be restored, and a deleted upstream can be brought back on its own. -The export and import options, how to set up each sync location, conflicts and restoring are described in [Sync and backup](/docs/lite/sync-and-backup). +It needs ThinkWatch Lite 2026.10.25 or later. The export and import options, how to set up each sync location, conflicts and restoring are described in [Sync and backup](/docs/lite/sync-and-backup). ## Menu bar, system tray and notifications diff --git a/src/content/docs-lite/en/import-links.md b/src/content/docs-lite/en/import-links.md index 58b4de8..ebbb391 100644 --- a/src/content/docs-lite/en/import-links.md +++ b/src/content/docs-lite/en/import-links.md @@ -215,10 +215,10 @@ The site fills in its server address and the key of the row, and the preset appe ## Generated by Lite -ThinkWatch Lite can also write an import link for an existing upstream, to hand it to another device or another person. In the ⋯ menu of an upstream row, **Share…** follows **View traffic** and opens the share dialog: +From 2026.10.25, ThinkWatch Lite can also write an import link for an existing upstream, to hand it to another device or another person. In the ⋯ menu of an upstream row, **Share…** follows **View traffic** and opens the share dialog: - The dialog shows the link in its web form. **Copy web link** and **Copy thinkwatch:// link** copy each form. On a device without Lite, the web link shows how to install it, and nothing after `#` is sent to the site's server. -- **Include keys** is on by default, and the dialog then notes: “The link contains the key. Anyone holding the link can use the key; send it only through a trusted channel.” With it off, the link carries no key, and the recipient fills it in in the dialog. +- **Include keys** is on by default, and the dialog then notes: “The link contains the key. Anyone holding the link can use the key; send it only through trusted channels.” With it off, the link carries no key, and the recipient fills it in in the dialog. - The foot of the dialog lists what the link includes and what it leaves out, for example “Includes: address, protocol, key, 2 models, balance source. Leaves out: outbound proxy, routes.” What the link carries: @@ -227,11 +227,12 @@ What the link carries: |---|---|---| | Service, authentication method, site, name, address, protocol (`service`, `auth`, `site`, `name`, `url`, `protocol`) | Yes | — | | Concurrency limit, client identity forwarding, balance source, model list, the models it is limited to, specs set by hand, billing mode, price multiplier, prices (`concurrency`, `client_identity`, `balance`, `models`, `models_only`, `specs`, `billing`, `price_multiplier`, `prices`) | Yes | Values equal to the defaults are left out | -| Key, headers, AWS credentials, OAuth client credentials | As set by **Include keys**, on by default | A value written as `${NAME}` cannot be written, since links do not accept that form; it is left out, with the reason | +| Key, headers, AWS access keys, OAuth credentials | As set by **Include keys**, on by default | A value written as `${NAME}` cannot be written, since links do not accept that form; it is left out, with the reason | | Outbound proxy, aliases, routes, weights, the disabled state, AWS profile | No | Import links do not accept them; see [What a link can never set](#what-a-link-can-never-set) | -| ChatGPT and Z.ai / BigModel account sign-ins | No | The recipient stops at the sign-in step and signs in | +| ChatGPT and Z.ai / BigModel account sign-ins | No | The link carries the sign-in method only; the recipient stops at the sign-in step and signs in | +| Tiered prices above 200K; values with characters links do not accept | No | Listed under “Leaves out” at the foot of the dialog | -When the link would be longer than 8192 characters, usually because of many prices or specs, the link form is unavailable and the dialog suggests a configuration pack instead. **Export as configuration pack…** opens the export dialog with the upstream checked, with passphrase encryption optional and off by default. The recipient imports it with **Import configuration…**, and the pack can also carry what a link cannot set, such as the upstream's outbound proxy; see [Sync and backup](/docs/lite/sync-and-backup#exporting). +When the link would be longer than 8192 characters, usually because of many prices or specs, the link form is unavailable; the dialog gives the link's length and suggests a config pack instead, as it does for an upstream that cannot be written as an import link. **Export as config pack…** opens the export dialog with the upstream checked, with passphrase encryption optional and off by default. The recipient imports it with **Import config…**, and the pack can also carry what a link cannot set, such as the upstream's outbound proxy; see [Sync and backup](/docs/lite/sync-and-backup#exporting). Links are written by the same rules they are read by: a generated link opened in the same version of the app fills in the New upstream dialog with the settings of the original upstream, apart from what the table above leaves out. diff --git a/src/content/docs-lite/en/sync-and-backup.md b/src/content/docs-lite/en/sync-and-backup.md index 2246cb4..4ccd053 100644 --- a/src/content/docs-lite/en/sync-and-backup.md +++ b/src/content/docs-lite/en/sync-and-backup.md @@ -2,18 +2,20 @@ After moving to a new computer or reinstalling, the ThinkWatch Lite configuration can be imported whole from one file instead of being entered again item by item. Several computers and cores on servers can also share one configuration through a sync location of their own choosing: an upstream added or a key rotated on one device reaches the others. Every change to the configuration leaves a version that can be restored. -This page covers configuration packs, backups, exporting and importing, turning on sync and setting up each kind of sync location, encryption, conflicts, restoring, and what is never exported or synced. Sharing a single upstream is described in [Import links](/docs/lite/import-links#generated-by-lite). +> What this page describes needs ThinkWatch Lite 2026.10.25 or later. With a remote core, the server needs core 0.78.0 or later. + +This page covers config packs, backups, exporting and importing, turning on sync and setting up each kind of sync location, encryption, conflicts, restoring, and what is never exported or synced. Sharing a single upstream is described in [Import links](/docs/lite/import-links#generated-by-lite). It is gathered in **Settings › Sync and backup**, which has a **Sync** part and a **Backups** part. While the app is connected to a remote core, the section sits under the server's configuration and manages the configuration on the server; see [With a remote core](#with-a-remote-core). -## Configuration packs +## Config packs -A configuration pack is a single file with the `.twconfig` extension that holds the configuration, the plugin scripts and Lite's general preferences. Exports, shares, the backup versions on the device and the versions in a sync location all use it. +A config pack is a single file with the `.twconfig` extension that holds the configuration, the plugin scripts and Lite's general preferences. Exports, shares, the backup versions on the device and the versions in a sync location all use it. - An unencrypted pack is a ZIP file. A pack encrypted with a passphrase is in the age format, with the key derived from the passphrase by scrypt, and decrypts to the same ZIP file. Both have the same extension and are told apart by their header. - A pack is at most 16 MiB and may hold only the expected kinds of file; any other path, an absolute path or `..` rejects the whole pack. A plugin file whose SHA-256 does not match the configuration's record of it is marked as an error. -- A pack exported by a newer version asks for an update first: “This configuration pack was exported by a newer version. Update ThinkWatch Lite first.” A pack from an older version is repaired as it is imported: fields this version does not know are removed and invalid values go back to their defaults, and the preview lists each repair. -- A wrong passphrase is reported as “Incorrect passphrase”. +- A pack exported by a newer version asks for an update first, with a message that names its Core version and format and ends “Update ThinkWatch Lite first.” A pack from an older version is repaired as it is imported: fields this version does not know are removed and invalid values go back to their defaults, and the preview lists each repair. +- A wrong passphrase is reported as “The passphrase is not correct.” ## Backups @@ -21,31 +23,31 @@ Backups are earlier versions of the configuration, kept in two places: | Where | When a version is made | Kept | |---|---|---| -| This device | On every change to the configuration: in the app, from the command line, by editing the file by hand, by sync, import, restore or **Repair**. Not when the content equals the previous version, and not when a ChatGPT token is renewed. | The last 10 versions | +| This device | On every change to the configuration: in the app, from the command line, by editing the file by hand, by sync, import, restore or one-click repair. Not when the content equals the previous version, and not when a ChatGPT token is renewed. | The last 10 versions | | Sync location | The versions each device uploads once sync is on | The latest and the previous version | -A version on the device is a complete configuration pack, with the plugins, the sign-in tokens of ChatGPT and OAuth upstreams, and the local settings (the control channel key masked), stored in `history/` in the data directory. The folder and files can be read only by their owner and are not encrypted, the same protection `config.yaml` itself has. These versions replace the former change history, and the history entry of the configuration file editor now opens the backup list. +A version on the device is a complete config pack, with the plugins, the sign-in tokens of ChatGPT and OAuth upstreams, and the local settings (the control channel key masked), stored in `history/` in the data directory. The folder and files can be read only by their owner and are not encrypted, the same protection `config.yaml` itself has. These versions replace the former version history, and **Backups** in the config file editor opens the same list. The old history files are removed when the first backup is kept after the upgrade. -The **Backups** part lists the versions newest first: the time, the source (such as **Changed in the app**, **Imported**, **File edited by hand** or **Synced from MacBook Air**), where it is stored (this device, the sync location), what it holds (such as “7 upstreams, 2 plugins”) and the Core version that made it; the version in use is marked **Current**. A version stored both on the device and in the sync location is one row with both places marked. Every other version has **Restore…**, described under [Restore](#restore), and **Save as…**, which saves it as a configuration pack, optionally encrypted with a passphrase. +The **Backups** part lists the versions newest first: the time, the source (such as **Edited in the app**, **Import**, **File edited by hand** or **Synced from MacBook Air**), where it is stored (**Local**, **Sync location**), what it holds (such as “7 upstreams, 2 plugins”) and the Core version that made it; the version in use is marked **Current**. A version stored both on the device and in the sync location is one row with both places marked. Every other version has **Restore…**, described under [Restore](#restore), and **Save as…**, which saves it as a config pack, optionally encrypted with a passphrase. -ThinkWatch Lite makes no scheduled backups and has no separate manual backup. To keep a copy elsewhere, use **Export configuration…** or **Save as…** on a version; the file can be stored anywhere. +ThinkWatch Lite makes no scheduled backups and has no separate manual backup. To keep a copy elsewhere, use **Export config…** or **Save as…** on a version; the file can be stored anywhere. ## Exporting There are three ways to export: -- **Export configuration…** in the **Backups** part, which exports the whole configuration by default; -- **Export selected…** on the toolbar of the Upstreams page, once upstreams are selected; -- **Export as configuration pack…** in the share dialog of an upstream. +- **Export config…** in the **Backups** part, which exports the entire config by default; +- **Export N selected upstreams…** on the toolbar of the Upstreams page, once upstreams are selected; +- **Export as config pack…** in the share dialog of an upstream. All three open the same export dialog. From the Upstreams page, the scope starts as **Selected items** with those upstreams checked. | Item | Description | |---|---| -| Scope | **Whole configuration** or **Selected items**. Selected items lists the upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, security rules, plugins and global settings by category. Items that the selected ones refer to are checked automatically and marked **Dependency**, such as the upstream of a route or the proxy of an upstream. | +| Scope | **Entire config** or **Selected items**. Selected items lists the upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, security rules, plugins and global settings by category. Items that the selected ones refer to are checked automatically and marked **Included**, such as the upstream of a route or the proxy of an upstream. | | Include keys | On by default. Covers upstream keys, header values, AWS credentials, OAuth client secrets, proxy passwords, gateway keys and the keys obtained by signing in to a Z.ai / BigModel account. Values written as `${NAME}` are exported as written, not expanded. | -| Encrypt with passphrase | Off by default. When on, the passphrase is entered twice and has at least 8 characters. It is not stored, and importing asks for the same passphrase. | -| Not exported | What this export leaves out: the sign-ins of ChatGPT accounts and of upstreams that use an OAuth refresh token (such as “ChatGPT sign-in (1 upstream); sign in again after importing”), the listening, remote control and sync settings, and the request records. | +| Encryption | **Encrypt the file with a passphrase**, off by default. When on, the passphrase is entered twice and has at least 8 characters. It is not stored, and importing asks for the same passphrase. | +| Not exported | What this export leaves out, such as “ChatGPT account and OAuth sign-ins (1 upstream); sign in or fill them in again after importing.”, “Listening, remote control and sync settings.” and “Request history.” With **Include keys** off, the upstream keys, gateway keys and proxy passwords are listed as well. | With keys included and no encryption, the foot of the dialog warns: “The file contains keys in plain text. Keep it safe.” @@ -53,9 +55,9 @@ With keys included and no encryption, the foot of the dialog warns: “The file There are three ways to import: -- **Import configuration…** in the **Backups** part; -- dropping a configuration pack onto the main window; -- on a new installation, whose configuration is still the initial one, **Import configuration…** next to **New upstream** on the empty Upstreams page. +- **Import config…** in the **Backups** part; +- dropping a config pack onto the main window; +- on a new installation, whose configuration is still the initial one, **Import config…** next to **New upstream** on the empty Upstreams page. An encrypted pack first asks for its passphrase. The dialog then states what the pack is (whole or partial, with or without keys, when it was exported and by which Core version) and lists each of its items by category with a status: @@ -63,21 +65,23 @@ An encrypted pack first asks for its passphrase. The dialog then states what the |---|---|---| | New | Nothing of that name exists | Imported; uncheck it to skip it | | Same | The same name with the same content | Nothing; counted in the summary | -| Conflict | The same name with different content | **Overwrite**, **Import renamed** or **Skip** must be chosen; there is no default | +| Conflict | The same name with different content | **Overwrite**, **Rename** or **Skip** must be chosen; there is no default | | Conflict (global settings) | That section was changed on this device and differs from the pack | **Overwrite** or **Skip** must be chosen | -| Key missing | The pack holds no keys; or the upstream uses an OAuth refresh token and no upstream of that name here uses the same token endpoint | Imported disabled, with a **Key missing** tag on the upstream row; it can be enabled once the key, or for an OAuth upstream the refresh token, is filled in | +| Key missing | The pack holds no keys and nothing of that name here has the same destination (see below); or the upstream uses an OAuth refresh token and no upstream of that name here uses the same token endpoint | Imported disabled, with a **Key missing** tag on the upstream row; it can be enabled once the key, or for an OAuth upstream the refresh token, is filled in | | Sign-in required | A ChatGPT upstream whose namesake here is not a ChatGPT account | Imported disabled, with **Sign in…** on the upstream row | | Plugin | A plugin script | Imported disabled; a plugin that may change tool calls is approved when it is turned on | -| Error | It refers to an AWS profile, proxy or upstream that does not exist here; a field is invalid; or a gateway key has the value of another existing key | Cannot be imported, with the reason | +| Error | It refers to an AWS profile, proxy or upstream that does not exist here; a field is invalid, such as an upstream address with a backslash or with a user name and password before the host; or a gateway key has the value of another existing key | Cannot be imported, with the reason | -**Import** stays unavailable until every conflict has a choice. **Overwrite all**, **Import all renamed** and **Skip all** above the list settle every conflict at once. +**Import** stays unavailable until every conflict has a choice. **Overwrite all**, **Rename all** and **Skip all** above the list settle every conflict at once. An import follows these rules: - Upstreams, proxies, gateway keys, aliases, groups, routes, price sheets, custom security rules and plugins are added one by one, by name. Items in the current configuration that the pack does not have stay as they are; an import never deletes anything. Replacing the whole configuration with another is a restore; see [Restore](#restore). - Each section of the global settings (failover, the state of each protection, the default route, the default key, auxiliary requests and the price sheet settings) is one item. When the section on this device is still at its defaults, the imported value is taken; otherwise a different imported value is a conflict. -- **Import renamed** proposes the original name with a number, such as “Company relay (2)”, which can be edited in the row and must not be taken. Routes, aliases and groups in the pack that refer to the item use the new name. +- **Rename** proposes the original name with a number, such as “Company relay (2)”, which can be edited in the row and must not be taken. Routes, aliases and groups in the pack that refer to the item use the new name. - **Overwrite** replaces the item of that name with the imported one. Routes and aliases on this device that refer to it stay as they are. Overwriting a ChatGPT upstream here, or an OAuth upstream with the same token endpoint, keeps this device's sign-in tokens. +- From a pack without keys, an overwrite keeps this device's key only while the destination stays the same: scheme, host and port, AWS region, OAuth token endpoint, and the outbound proxy it goes through (for a proxy itself, its address). Otherwise the row notes that the address differs from the one here, so the key here is not reused, and the upstream is imported disabled until a key is filled in. +- **Rename** never reuses the key, proxy password or sign-in here: an upstream imported under a new name stays disabled until a key is filled in or it is signed in again, and a plugin imported under a new name is disabled. - A route, alias or group that refers to a skipped upstream is marked as an error, naming the missing upstream. An upstream imported under a new name does not count as missing. - When the pack holds no keys, the imported gateway keys are generated anew. - An import is recorded as a version in the backups, so the configuration from before it can be restored. When the configuration changes after the preview, the import asks for a new preview. @@ -89,20 +93,22 @@ Sync lets several devices share one configuration. A device is one Core taking p | Type | Storage | Connection settings | |---|---|---| | Folder | A folder synced by iCloud Drive, Dropbox, OneDrive, Google Drive, Syncthing and the like, or a NAS share | The folder path | -| WebDAV | Jianguoyun (Nutstore), Nextcloud, Synology NAS, InfiniCloud and others | Address, user name and password (or app password) | -| S3-compatible storage | Amazon S3, Cloudflare R2, Alibaba Cloud OSS, Tencent Cloud COS, MinIO and others | Endpoint, region, bucket, prefix, Access Key ID and Secret Access Key | +| WebDAV | Jianguoyun (Nutstore), Nextcloud, Synology NAS, InfiniCloud and others | URL, user name and password (or app password) | +| S3-compatible | Amazon S3, Cloudflare R2, Alibaba Cloud OSS, Tencent Cloud COS, MinIO and others | Endpoint, region, bucket, prefix, access key ID and secret access key | -Requests to WebDAV and S3-compatible storage go out directly (the default), through the system proxy or through one of the outbound proxies. For both, plain `http://` is accepted only for this computer and private networks, by the same rules as [import links](/docs/lite/import-links#connection); any other address needs `https://`, and its certificate is always verified. Redirects are not followed, so that the sync location's password is never carried to another host; the address entered must be the final one. +Requests to WebDAV and S3-compatible storage go out **Direct** (the default), through the **System proxy** or through one of the outbound proxies, as chosen under **Proxy**. For both, plain `http://` is accepted only for this computer and private networks, by the same rules as [import links](/docs/lite/import-links#connection), and only direct, so that a password never crosses a proxy unencrypted; any other address needs `https://`, and its certificate is always verified. Redirects are not followed, so that the sync location's password is never carried to another host; the address entered must be the final one. -While sync is off, the **Sync** part holds only **Turn on sync…** and one line: “Devices share one configuration, and the sync location keeps the latest and the previous version.” Turning sync on takes three steps: +While sync is off, the **Sync** part holds only **Turn on sync…** and one line: “Devices share one configuration; the sync location keeps the latest and the previous version.” Turning sync on takes three steps: 1. **Sync location.** Pick the type and fill in the connection settings; **Test connection** checks that the location can be read and written. An empty location leads to step 2 to set up encryption. A location that already holds sync data leads to step 2 for its sync passphrase when it is encrypted, and skips step 2 when it is not. -2. **Encryption.** Starting a new sync, choose to encrypt (the default) or not, and enter the sync passphrase twice; it has at least 12 characters. Joining an existing sync, enter the passphrase once; the devices already syncing are listed with the time each last synced. A wrong passphrase is reported as “Incorrect sync passphrase”. -3. **Merge.** Only when joining an existing sync from a device that already has a configuration. The items are previewed by the rules of [importing](#importing), and each conflict is resolved with overwrite, rename or skip; alternatively, **Replace this device's configuration with the synced data**. +2. **Encryption.** Starting a new sync, choose **Encrypt with a passphrase** (the default) or **Do not encrypt**, and enter the sync passphrase twice; it has at least 12 characters. Joining an existing sync, enter the passphrase once; the devices already syncing are listed with the time each last synced. A wrong passphrase is reported as “The sync passphrase is not correct.” +3. **Merge.** Only when joining an existing sync from a device that already has a configuration. The items are previewed by the rules of [importing](#importing), and each conflict is resolved with overwrite, rename or skip; alternatively, **Replace this device's configuration with the sync data**. + +The default gateway key that every installation creates needs no choice in the merge: the joining device takes the value from the sync data, and clients connected on it are updated automatically. -A device whose configuration is still the initial one, a new installation that has not been changed, skips step 3 and takes the synced data as it is. A new installation offers **Join sync…** on its empty Upstreams page, which opens these steps directly. The configuration from before joining is kept in the backups. The device's name starts as the computer's name and can be changed. +A device whose configuration is still the initial one, a new installation that has not been changed, skips step 3 and takes the sync data as it is. A new installation offers **Join sync…** on its empty Upstreams page, which opens these steps directly. The configuration from before joining is kept in the backups. The **Device name** starts as the computer's name and can be changed. When two devices turn sync on in the same empty location at the same moment, the one that finishes second is asked to test the connection again and continues by joining. -Once sync is on, the **Sync** part shows the state (such as “Synced · 2 minutes ago”), the sync location with its connection settings, the outbound proxy, encryption, the upstreams not synced and this device's name, followed by the devices taking part, each with its name, last sync, Core version and state. **Sync now** checks at once. The sync settings are written to `config.yaml` and take effect when **Save** is chosen. +Once sync is on, the **Sync** part shows the state (such as “Synced · 2 minutes ago”), the sync location with its connection settings, the proxy, encryption, the upstreams not synced and the device name, followed by the devices taking part, each with its name, last sync, Core version and state. **Sync now** checks at once. The sync settings are written to `config.yaml` and take effect when **Save** is chosen. Changing the connection settings is for the same location, such as a new password; moving to another sync location means stopping sync and turning it on again. ### When devices sync @@ -121,7 +127,7 @@ Every device connects to the same location: the same folder in the same cloud dr ### iCloud Drive, Dropbox, OneDrive and other folders -Choose **Folder** and pick, in the folder dialog, the folder created for ThinkWatch in the cloud drive; pick the same folder on every device. The drive's own client carries the files between devices, and ThinkWatch only reads and writes that folder. +Choose **Folder** and pick, with **Choose…**, the folder created for ThinkWatch in the cloud drive; pick the same folder on every device. The drive's own client carries the files between devices, and ThinkWatch only reads and writes that folder. | Drive | Where its folder usually is | |---|---| @@ -139,31 +145,31 @@ ThinkWatch writes each version under a temporary name and renames it when it is 1. Create a folder in Jianguoyun, such as `ThinkWatch`. 2. In the Jianguoyun web app, open the security options of the account settings and add an application under third-party app management; Jianguoyun generates an app password for it. -3. In ThinkWatch Lite, choose **WebDAV**. The address is `https://dav.jianguoyun.com/dav/ThinkWatch/`, the user name is the Jianguoyun account, and the password is the app password from step 2, not the account password. +3. In ThinkWatch Lite, choose **WebDAV**. The URL is `https://dav.jianguoyun.com/dav/ThinkWatch/`, the user name is the Jianguoyun account, and the password is the app password from step 2, not the account password. ### Nextcloud 1. Create a folder in Nextcloud, such as `ThinkWatch`. 2. Create an app password on the Security page of the personal settings. An account with two-factor authentication must use an app password. -3. The address is `https:///remote.php/dav/files//ThinkWatch/`, with the Nextcloud user name and the app password from step 2. The Files settings in Nextcloud's Files app also show the WebDAV address. +3. The URL is `https:///remote.php/dav/files//ThinkWatch/`, with the Nextcloud user name and the app password from step 2. The Files settings in Nextcloud's Files app also show the WebDAV address. ### Synology NAS 1. Install WebDAV Server from the DSM Package Center and enable HTTPS, on port 5006 by default. 2. Create a subfolder in a shared folder, such as `ThinkWatch`. A DSM account of its own for ThinkWatch, with read and write access to that folder only, is recommended. -3. The address is `https://:5006//ThinkWatch/`, with that DSM account's user name and password. +3. The URL is `https://:5006//ThinkWatch/`, with that DSM account's user name and password. ThinkWatch always verifies the TLS certificate, and the self-signed certificate DSM comes with does not pass. A Let's Encrypt certificate for the NAS's domain can be obtained in DSM under **Control Panel › Security › Certificate**. For use on the local network only, `http://:5005/…` works too; the password, and the configuration when it is not encrypted, then cross the local network unencrypted. ### Addressing on S3-compatible storage -The endpoint decides how the bucket is addressed, with nothing to set: when the endpoint's host ends in `amazonaws.com`, `aliyuncs.com` or `myqcloud.com`, that is Amazon S3, Alibaba Cloud OSS and Tencent Cloud COS, the bucket goes in the host name (virtual-hosted style); every other endpoint, such as Cloudflare R2, MinIO or an IP address, puts it in the path (path style). +The endpoint decides how the bucket is addressed, with nothing to set: when the endpoint's host ends in `amazonaws.com`, `aliyuncs.com` or `myqcloud.com`, that is Amazon S3, Alibaba Cloud OSS and Tencent Cloud COS, the bucket goes in the host name (virtual-hosted style); every other endpoint, such as Cloudflare R2, MinIO or an IP address, puts it in the path (path style). A bucket name uses lowercase letters, digits, dots and hyphens; the prefix is a folder inside the bucket and cannot start with `/` or contain `.` or `..` segments. ### Cloudflare R2 1. Create a bucket in R2 in the Cloudflare dashboard, without public access. -2. Create an API token in R2's API token management, with object read and write permission applied to that bucket only. It gives an Access Key ID and a Secret Access Key. -3. In ThinkWatch Lite, choose **S3-compatible storage**: +2. Create an API token in R2's API token management, with object read and write permission applied to that bucket only. It gives an access key ID and a secret access key. +3. In ThinkWatch Lite, choose **S3-compatible**: | Field | Value | |---|---| @@ -171,7 +177,7 @@ The endpoint decides how the bucket is addressed, with nothing to set: when the | Region | `auto` | | Bucket | The bucket from step 1 | | Prefix | Empty, or a path such as `thinkwatch/` to keep it apart from other content in the bucket | -| Access Key ID, Secret Access Key | The values from step 2 | +| Access key ID, secret access key | The values from step 2 | ### Alibaba Cloud OSS @@ -180,7 +186,7 @@ The endpoint decides how the bucket is addressed, with nothing to set: when the | Endpoint | The S3-compatible address of OSS, `https://s3.oss-.aliyuncs.com`; for China (Hangzhou), `https://s3.oss-cn-hangzhou.aliyuncs.com` | | Region | The region ID, such as `cn-hangzhou` | | Bucket | The bucket name | -| Access Key ID, Secret Access Key | The AccessKey ID and AccessKey secret of a RAM user | +| Access key ID, secret access key | The AccessKey ID and AccessKey secret of a RAM user | A RAM user of its own for ThinkWatch, allowed to read and write that bucket only, is recommended, and the bucket's access stays private. @@ -191,7 +197,7 @@ A RAM user of its own for ThinkWatch, allowed to read and write that bucket only | Endpoint | `https://cos..myqcloud.com`; for Guangzhou, `https://cos.ap-guangzhou.myqcloud.com` | | Region | The region code, such as `ap-guangzhou` | | Bucket | The full bucket name with its APPID, in the form `-` | -| Access Key ID, Secret Access Key | The SecretId and SecretKey of an API key in Cloud Access Management | +| Access key ID, secret access key | The SecretId and SecretKey of an API key in Cloud Access Management | The key of a sub-user allowed to read and write that bucket only is recommended, and the bucket's access stays private. @@ -202,7 +208,7 @@ The key of a sub-user allowed to read and write that bucket only is recommended, | Endpoint | The S3 API address of MinIO, such as `https://minio.example.com:9000`, or `http://192.168.1.20:9000` for use on the local network only. The default port is 9000, not the console's port. MinIO is addressed in path style, so its buckets need no domain names | | Region | The region set in MinIO, or `us-east-1` when none is set | | Bucket | A bucket created beforehand | -| Access Key ID, Secret Access Key | An access key created for ThinkWatch, with a policy that allows reading and writing that bucket only | +| Access key ID, secret access key | An access key created for ThinkWatch, with a policy that allows reading and writing that bucket only | ### Other S3-compatible storage @@ -212,21 +218,22 @@ The endpoint is the service's S3 API address, `https://s3..amazonaws.com - Encryption is chosen when sync is turned on, encrypted by default, and every device on one sync location uses the same setting. Each device asks for the sync passphrase once, when it joins. The passphrase and the sync location's credentials are stored in the `sync` section of `config.yaml` on the device, like every other key, and are never exported or synced. - With encryption, the first device generates a random key, encrypts it with the sync passphrase and stores it in the sync location as `keyring`; every version and device record is encrypted with that key, in the age format. The sync location's provider sees only the file names (the version number and the first 8 characters of a device ID), the file sizes and their modification times. The authenticated encryption also guarantees that a version has not been tampered with: without the passphrase, no one can write a valid version. -- Without encryption, a version is a plain configuration pack. The provider of the sync location can read the configuration and the keys in it, and anyone who can write to the location can write a version and change the configuration. Choosing **Don't encrypt** when turning sync on shows this warning. To limit what tampering can do, a plugin added or with its script changed by sync starts disabled on this device and is turned on here, and an upstream whose address changed through sync is marked **Address changed by sync** once on its row. An unencrypted version carries a SHA-256 checksum, which only detects damage in transit. -- Turning encryption on later: once a sync passphrase is set, the latest version is uploaded again encrypted and the unencrypted versions are deleted; the other devices ask for the passphrase at their next sync. Turning it off needs the current passphrase and uploads the version again unencrypted. -- **Change sync passphrase…** re-encrypts only `keyring` and leaves the versions as they are. The other devices then show “The sync passphrase was changed on another device” and continue once the new passphrase is entered. -- If the sync passphrase is lost and no device is still syncing, the contents of the sync location cannot be decrypted; the only way forward is to stop syncing and turn sync on again. The configuration and backups on each device are not affected. +- Without encryption, a version is a plain config pack. The provider of the sync location can read the configuration and the keys in it, and anyone who can write to the location can write a version and change the configuration. Choosing **Do not encrypt** when turning sync on shows this warning. To limit what tampering can do, a plugin added or with its script changed by sync starts disabled on this device and is turned on here, and an upstream whose address changed through sync is marked **URL changed by sync** once on its row. An unencrypted version carries a SHA-256 checksum, which only detects damage in transit. +- Turning encryption on later: once a sync passphrase is set, the latest version is uploaded again encrypted and the unencrypted versions are deleted; the other devices ask for the passphrase at their next sync. Turning it off needs the current passphrase and uploads the version again unencrypted; the sync passphrase saved on each device is kept, to check the sync key with. +- An encrypted location never accepts unencrypted versions and never turns encryption off by itself: when the sync key (`keyring`) goes missing or is replaced by an older one, syncing pauses instead; see [Sync states](#sync-states). +- **Change passphrase…** re-encrypts only `keyring` and leaves the versions as they are. The other devices then show “Sync passphrase changed on another device” and continue once the new passphrase is entered. +- If the sync passphrase is lost and no device is still syncing, the contents of the sync location cannot be decrypted; the only way forward is to stop syncing and turn sync on again. The configuration and backups on each device are not affected. Each device keeps its own copy of the sync key, which can put it back when the one in the sync location is lost. ## What is synced Everything in the configuration is synced, keys included, except local settings, the sign-in tokens of ChatGPT and OAuth upstreams, and data such as request records. The full list is under [Never exported or synced](#never-exported-or-synced). -- **ChatGPT upstreams** appear disabled on a new device, marked **Sign-in required**. The sign-in tokens and the account ID that comes with a sign-in stay on each device and are never compared; signing in, renewing and signing out do not trigger a sync. Each device can sign in to a different ChatGPT account. -- **Upstreams that use an OAuth refresh token** are signed in on each device, as ChatGPT upstreams are. The token endpoint, client ID and client secret are synced; the refresh and access tokens stay on each device, and refreshing them triggers no sync. A device that has no token yet needs its own refresh token filled in before it can use the upstream. Many OAuth services issue a new refresh token on every refresh and invalidate the old one, so one token shared by two devices would invalidate itself. -- **Upstreams not synced** are chosen in the sync settings under **Upstreams not synced**, a local setting. They are not uploaded, and an upstream of the same name from another device does not replace them, which suits an Ollama at a local address. Routes, aliases and groups that refer to them are not synced either and are listed in the sync settings under **Also not synced**. While sync is on, their rows carry a **Not synced** tag. +- **ChatGPT upstreams** appear disabled on a new device, marked **Sign-in required**, and stay off there until they are signed in on that device; this turns them off nowhere else. The sign-in tokens and the account ID that comes with a sign-in stay on each device and are never compared; signing in, renewing and signing out do not trigger a sync. Each device can sign in to a different ChatGPT account. +- **Upstreams that use an OAuth refresh token** are signed in on each device, as ChatGPT upstreams are. The token endpoint, client ID and client secret are synced; the refresh and access tokens stay on each device, and refreshing them triggers no sync. On a device that has no token yet, the upstream stays off until that device's own refresh token is filled in; the other devices are not affected. Many OAuth services issue a new refresh token on every refresh and invalidate the old one, so one token shared by two devices would invalidate itself. +- **Upstreams not synced** are chosen in the sync settings under **Upstreams not synced**, a local setting. They are not uploaded, and an upstream of the same name from another device does not replace them, which suits an Ollama at a local address. Routes, aliases and groups that refer to them are not synced either and are listed in the sync settings after “Not synced with them:”. While sync is on, their rows carry a **Not synced** tag. - **Plugins** are synced, and each stays turned on or off alike on every device; a plugin that may change tool calls runs only once it is approved on each device. The scripts of the plugins built into Core come from each device's Core and are upgraded with it, so they are not synced; whether they are on and their settings are. Different Core versions on the devices cause no sync because of their built-in plugins. - **Environment variables** written as `${NAME}` are synced as written. On a device where the variable is not set, requests to that upstream fail there, the upstream row says why, and the rest of the configuration is unaffected. -- **Lite's general preferences**, the language, appearance, menu bar item, notifications and update checks, stay the same on every device, and a change made on another device takes effect at once. +- **Lite's general preferences**, the language, appearance, menu bar item, notifications and update checks, stay the same on every device, and a change made on another device takes effect at once. When the general preferences in the sync data exceed 64 KiB, this device keeps its own and the rest is synced as usual. ## Conflicts @@ -239,7 +246,7 @@ Each version records the version it was based on. A merge takes the last version | Upstream | Connection (service, address, protocol, authentication method, key, headers, outbound proxy, AWS settings); models (model list, the models it is limited to, models added by hand, specs); billing (billing mode, prices, price multiplier); other (concurrency limit, client identity forwarding, balance source, disabled state) | | Proxy, gateway key, alias, group, route, price sheet, custom security rule | The whole item | | Plugin | Script; settings; on or off | -| Route order | One unit | +| Upstream order, route order, plugin order | One unit each | | Global settings, Lite's general preferences | Each field | When two devices change different units of the same item, the changes merge by themselves: one device rotates an upstream's key while the other changes its prices, for instance. A conflict arises only when the same unit is changed differently on both sides, or deleted on one side and changed on the other. A rename counts as deleting the old name and adding the new one. A merged result must pass the configuration's checks, such as a route's upstream existing, and an item that fails them is treated as a conflict. @@ -250,8 +257,8 @@ When two devices upload at the same moment, the sync location holds two versions - A conflict does not stop syncing: everything else is merged and uploaded as usual, and each device keeps its own content for the conflicting items. - The conflicts travel with the versions, so every device involved shows the same ones. Once they are resolved on any device, the result is uploaded as a new version and the others clear them; nothing is resolved twice. -- The **Sync** part and the top of the Upstreams page show a yellow notice, such as “2 sync conflicts”, which opens the **Sync conflicts** dialog. The notice can be ignored; the conflicts remain until they are resolved. -- The dialog lists each item with the time and device of each side's change and the fields that differ; clicking an item's name shows the difference. In the **Use** column, each item takes this device's version, the other device's, or **Keep both**: this device's content keeps the name, and the other side's is added as “Name (device name)”, which can be edited. Global settings, the route order and Lite's general preferences cannot keep both. **Later** closes the dialog without changes, and **Apply** writes the choices. +- The **Sync** part and the top of the Upstreams page show a yellow notice, such as “2 sync conflicts”, whose **Resolve…** opens the **Sync conflicts** dialog. The notice can be ignored; the conflicts remain until they are resolved. +- The dialog lists each item with the time and device of each side's change and the fields that differ; clicking an item's name shows the difference. In the **Use** column, each item takes **This device**, the other device (shown by its name) or **Keep both**: this device's content keeps the name, and the other side's is added as “Name (device name)”, which can be edited. Global settings, the orders, Lite's general preferences and items deleted on one side cannot keep both. **Later** closes the dialog without changes; **Apply** writes the items that have a choice and leaves the rest for later. ## Sync states @@ -259,36 +266,43 @@ When two devices upload at the same moment, the sync location holds two versions |---|---|---| | Synced | “Synced · 2 minutes ago” in the settings; nothing on other pages | — | | Syncing | “Syncing” in the settings | — | -| Conflict | A yellow notice such as “2 sync conflicts” | Resolve the conflicts | -| Sync location unavailable | After 2 hours of failures in a row, a yellow notice “Cannot reach the sync location” with the reason | Nothing: syncing resumes by itself, and the changes made here are uploaded then | -| Authentication failed | A yellow notice “The sync location denied access” | Correct the connection settings | -| Passphrase changed | A yellow notice “The sync passphrase was changed on another device” | Enter the new passphrase | -| Newer version | A yellow notice “Another device uses a newer version. Update ThinkWatch Lite to continue syncing.”; the device lists of the other devices show this one as “Paused: update needed” | Update ThinkWatch Lite | - -Nothing raises a system notification while sync runs normally. A state that needs attention raises one, following the notice setting in **Settings › General**, and every yellow notice can be ignored. +| Conflict | A yellow notice such as “2 sync conflicts”; “2 conflicts” in the settings | **Resolve…** | +| Sync location unreachable | After 2 hours of failures in a row, a yellow notice “Sync location unreachable” with the reason | Nothing: syncing resumes by itself, and the changes made here are uploaded then | +| Access denied | A yellow notice “Sync location denied access” | Correct the connection settings | +| Passphrase changed | A yellow notice “Sync passphrase changed on another device” | **Enter passphrase…** with the new one | +| Newer version | A yellow notice “Sync paused”: “Another device uses a newer version. Update ThinkWatch Lite to continue syncing.” The device lists of the other devices show this one as “Paused: update required” | **Check for updates** and update ThinkWatch Lite | +| Sync key unavailable | A yellow notice “Sync key at the sync location unavailable”: the sync key is missing, was replaced by an older one or cannot be read, and syncing is paused | **Upload this device's configuration**, which also puts back the sync key saved on this device, or **Stop syncing…** | +| Sync location empty | A yellow notice “Sync location empty”: another device may have deleted the sync data, or the location is no longer the same place, and syncing is paused. A location that reads as empty is checked a few more times first | **Upload this device's configuration**, or **Stop syncing…** | +| Sync settings unusable | A yellow notice “Sync settings unusable”, such as when the chosen outbound proxy no longer exists | Correct the connection details or the proxy | +| Safe mode | “Paused: safe mode” in the settings | Nothing: syncing continues once the configuration can be read again | + +None of these states turns sync off by itself; stopping is always the user's choice. Nothing raises a system notification while sync runs normally. A state that needs attention raises one, following the notice setting in **Settings › General**, and every yellow notice can be ignored. Files at the sync location that cannot be used are skipped, and the state says how many. When the latest version in the sync location was written by a newer Core that this device cannot read, the device pauses syncing and neither downloads nor uploads. Changes made in the meantime stay on the device and are merged by the usual rules after the update, so an older Core never overwrites fields a newer Core wrote. A remote core is updated on its server; the device list shows each device's Core version. -A device record not updated for 180 days is removed automatically. Nothing syncs automatically in safe mode. +A device record not updated for 180 days is removed automatically. ## Stopping sync **Stop syncing…** offers two ways: - **Stop syncing**: the device keeps its current configuration, drops its sync settings and removes its device record from the sync location. The other devices are not affected. -- **Stop syncing and delete sync data**, after a further confirmation, deletes every version, every device record and `keyring` from the sync location. The other devices then report the location as empty and stop syncing. +- **Stop and delete sync data…**, after a further confirmation, deletes every version, every device record and `keyring` from the sync location. It cannot be undone; this device's configuration and local backups are not affected. The other devices then report the location as empty, where each can upload its configuration again or stop syncing. ## Restore - **Restore…** on a version in the **Backups** list opens a preview of the differences between that version and the current configuration, item by item: added, removed or changed. Every item is checked by default, which replaces the current configuration with the version as a whole; an unchecked item keeps its current content. To bring back one deleted upstream, check only that one. +- An unchecked item that a checked one refers to is restored with it, and an item that would be removed but is still referred to by items that stay is marked “Kept: still referred to by items that stay”. The preview and the write follow the same plan. - Local settings keep their current values. A ChatGPT upstream of the same name, and an OAuth upstream with the same token endpoint, keep their current sign-in tokens rather than the older ones in the backup, which a refresh may already have invalidated. - A restore is itself recorded as a new version, so it can be undone. -- While sync is on, the restored configuration is uploaded as a new version and the other devices follow; the foot of the preview says so, for example “The restored configuration will sync to 2 other devices”. Upstreams not synced are not affected. +- While sync is on, the restored configuration is uploaded as a new version and the other devices follow; the foot of the preview says so, for example “The restored config will sync to 2 other devices.” Upstreams not synced are not affected. ### When the configuration cannot be read When the configuration cannot be read and the app runs in safe mode, the error page offers **Restore from backup…** next to **Repair**. It lists the versions on the device, and those in the sync location when it can be reached, and opens the restore preview for the chosen one. When the configuration cannot be read at all, a restore brings back the whole version, keeping only the current control channel key. An upgrade does not rewrite the configuration and therefore makes no version of its own: a configuration that cannot be read after an upgrade is the latest version, and the content from before a repair is kept in the backups as well. +From 2026.10.25, an upstream address may not contain a backslash or carry a user name or password before the host. A configuration with such an address opens in safe mode after the upgrade; the backups hold the same address, so the fix is to edit the configuration file and move the credential into the key or a header. + ## Connected clients Each client connected on this device has a gateway key written into its configuration file. When a sync, a restore or an import with **Overwrite** changes gateway keys, the following keeps those clients working: @@ -296,15 +310,16 @@ Each client connected on this device has a gateway key written into its configur | Situation | What happens | |---|---| | The value of a gateway key a client uses is replaced | The configuration of those clients is rewritten, as when a key is rotated on the Keys page | -| A gateway key a client uses would be deleted | Sync: the key is kept and listed as a conflict, with a note such as “Used by 2 clients on this device”. Restore: the item is marked **Kept** in the preview and not deleted | -| **Replace this device's configuration with the synced data** when joining a sync | The keys clients on this device use are added to the synced data and then synced to the other devices; the joining steps say so | +| A gateway key a client uses would be deleted | Sync: the key is kept and listed as a conflict, noting “A client on this device uses this gateway key; it was not deleted.” Restore: the item is marked “Kept: in use by a client on this device” in the preview and not deleted | +| The default gateway keys differ when joining a sync | The value from the sync data is used, and clients connected on this device are updated automatically | +| **Replace this device's configuration with the sync data** when joining a sync | The keys clients on this device use are added to the sync data and then synced to the other devices; the joining steps say so | With a remote core, the Lite connected to it does the same when it is told of the change. ## With a remote core - **Sync and backup** sits under the server's configuration in the settings, and backups, exports, imports and sync all apply to the configuration on the server. -- Exports and imports go through the control channel, and the configuration pack is saved and opened on the computer running Lite. +- Exports and imports go through the control channel, and the config pack is saved and opened on the computer running Lite. - A remote core can join a sync too. The server itself connects to the sync location, and a folder path is a path on the server, typed in by hand; the folder dialog is available only with the local core. - Lite's general preferences stay on the computer running Lite and are not handed to the remote core: Lite adds them to an export and applies them on import, and they take no part in the remote core's backups or sync. @@ -323,6 +338,7 @@ twcore backup restore twcore sync status twcore sync now twcore sync enable +twcore sync republish twcore sync disable --purge ``` @@ -330,7 +346,7 @@ twcore sync disable --purge - `import` adds what is not here and asks about each item that differs; `--on-conflict overwrite|rename|skip` answers for all of them, and `--dry-run` shows what the import would do without writing. An encrypted pack asks for its passphrase or reads `TWCORE_PASSPHRASE` as well, and a pack from an older version lists its repairs first. - `backup list` lists the versions newest first, with their ids; `backup restore` brings back a whole version, and `--dry-run` lists what would change. A restore keeps the local settings, the ChatGPT sign-ins and the gateway keys that clients on this machine use. - `export`, `import` and `backup` work on the files in the data directory, so they work while core is not running, including when a broken configuration keeps it from starting; a running core picks their changes up within a second. -- `sync` talks to the running core over the control channel: `status` prints the state, the location and the devices, `now` syncs once, `enable` asks for the sync location and passphrase interactively, and `disable` stops syncing with this device keeping its configuration; `--purge` also deletes every version, device record and `keyring` from the sync location. +- `sync` talks to the running core over the control channel: `status` prints the state, the location and the devices, `now` syncs once, `enable` asks for the sync location and passphrase interactively, `republish` uploads this device's configuration again after the sync location was emptied or lost its keyring (and puts back the keyring this device keeps), and `disable` stops syncing with this device keeping its configuration; `--purge` also deletes every version, device record and `keyring` from the sync location. On a server set up by the install script, the commands start with `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch`, like those in [Connecting to a remote core](/docs/lite/remote-core). `twcore --help` lists every option. diff --git a/src/content/docs-lite/zh-CN/features.md b/src/content/docs-lite/zh-CN/features.md index b873c51..267cc14 100644 --- a/src/content/docs-lite/zh-CN/features.md +++ b/src/content/docs-lite/zh-CN/features.md @@ -61,7 +61,7 @@ WSL 2 默认使用 NAT 网络,此时 Windows 上的网关无法从 WSL 内访 API 密钥和请求头的值可以写成 `${变量名}`,读取系统环境变量。macOS 上读的是登录 shell 里的环境变量,`~/.zshrc` 等文件中 `export` 的变量都会生效,Linux 同理(`~/.bashrc`、`~/.profile` 等);Windows 上读的是系统设置里配置的环境变量。修改变量后,重新打开应用即可生效。代理相关的变量(`HTTPS_PROXY` 等)和 `PATH` 不会被读取。 -中转站、服务商或 ThinkWatch 企业版控制台可以提供导入链接(`thinkwatch://import?…`,或网页形式 `https://thinkwat.ch/import#…`),打开预填好的「新建上游」对话框,服务类型、地址、密钥、请求头、并发上限、模型、模型规格与价目表都可以预先填写。对话框写明接收请求与密钥的主机;选择「下一步」或开始登录之前,不写入配置,也不发出任何请求。一条链接只能新增一个上游,不能设置代理、模型别名、路由或环境变量引用;需要更新版本应用的链接会提示更新。各服务类型的参数与链接生成器见[导入链接](/zh-CN/docs/lite/import-links)。上游行菜单中的「分享…」为已有的上游生成导入链接,可以选择是否包含密钥;多选上游之后,工具条上的「导出所选…」把它们导出为配置包(见[同步与备份](#同步与备份))。 +中转站、服务商或 ThinkWatch 企业版控制台可以提供导入链接(`thinkwatch://import?…`,或网页形式 `https://thinkwat.ch/import#…`),打开预填好的「新建上游」对话框,服务类型、地址、密钥、请求头、并发上限、模型、模型规格与价目表都可以预先填写。对话框写明接收请求与密钥的主机;选择「下一步」或开始登录之前,不写入配置,也不发出任何请求。一条链接只能新增一个上游,不能设置代理、模型别名、路由或环境变量引用;需要更新版本应用的链接会提示更新。各服务类型的参数与链接生成器见[导入链接](/zh-CN/docs/lite/import-links)。上游行菜单中的「分享…」为已有的上游生成导入链接,可以选择是否包含密钥;多选上游之后,工具条上的「导出所选 N 个上游…」把它们导出为配置包(见[同步与备份](#同步与备份))。 ## 路由与故障转移 @@ -111,7 +111,7 @@ MCP 页管理客户端从自己的配置文件中加载的内容,这些内容 换电脑、重装或在多台设备上使用时,配置不必重新填写:可以导出为一个文件再导入,也可以经自选的文件夹、WebDAV 或 S3 兼容存储在各设备之间自动同步,同步内容缺省以口令加密。每次配置变更都留下一个可以恢复的版本,误删的上游也可以单独找回。 -导出与导入的选项、各同步位置的配置方法、冲突与恢复见[同步与备份](/zh-CN/docs/lite/sync-and-backup)。 +这些功能需要 ThinkWatch Lite 2026.10.25 或更高版本;导出与导入的选项、各同步位置的配置方法、冲突与恢复见[同步与备份](/zh-CN/docs/lite/sync-and-backup)。 ## 菜单栏、系统托盘与通知 diff --git a/src/content/docs-lite/zh-CN/import-links.md b/src/content/docs-lite/zh-CN/import-links.md index 09e78f9..d04a427 100644 --- a/src/content/docs-lite/zh-CN/import-links.md +++ b/src/content/docs-lite/zh-CN/import-links.md @@ -215,7 +215,7 @@ thinkwatch://import?service=newapi&url={address}&key={key} ## 由 Lite 生成 -ThinkWatch Lite 也能为已有的上游生成导入链接,把一个上游交给另一台设备或另一位用户。上游行的 ⋯ 菜单中,「查看流量」之后是「分享…」,它打开「分享上游」对话框: +ThinkWatch Lite 2026.10.25 起也能为已有的上游生成导入链接,把一个上游交给另一台设备或另一位用户。上游行的 ⋯ 菜单中,「查看流量」之后是「分享…」,它打开「分享上游」对话框: - 对话框显示网页形式的链接,「复制网页链接」与「复制 thinkwatch:// 链接」分别复制两种形式。网页链接在未安装 Lite 的设备上显示安装说明,`#` 之后的内容不发送至网站服务器。 - 「包含密钥」缺省开启,此时对话框提示「链接包含密钥。持有链接即可使用该密钥,请仅通过可信渠道发送。」关闭后链接不含密钥,接收方在对话框中自行填写。 @@ -227,11 +227,12 @@ ThinkWatch Lite 也能为已有的上游生成导入链接,把一个上游交 |---|---|---| | 服务类型、认证方式、站点、名称、地址、协议(`service`、`auth`、`site`、`name`、`url`、`protocol`) | 写入 | — | | 并发上限、转发客户端身份、余额来源、模型列表、只用这些模型、手动规格、计费方式、价格倍率、价目(`concurrency`、`client_identity`、`balance`、`models`、`models_only`、`specs`、`billing`、`price_multiplier`、`prices`) | 写入 | 与缺省值相同的不写 | -| 密钥、请求头、AWS 凭据、OAuth 客户端凭据 | 由「包含密钥」决定,缺省写入 | 值写成 `${变量名}` 时不能写入,因为链接不接受这种写法;该项省略,并注明原因 | +| 密钥、请求头、AWS 访问密钥、OAuth 凭据 | 由「包含密钥」决定,缺省写入 | 值写成 `${变量名}` 时不能写入,因为链接不接受这种写法;该项省略,并注明原因 | | 出站代理、别名、路由、权重、停用状态、AWS profile | 不写入 | 导入链接不接受这些参数,见[链接不能设置的内容](#链接不能设置的内容) | -| ChatGPT、Z.ai / BigModel 的账号登录凭据 | 不写入 | 接收方停在登录一步,自行登录 | +| ChatGPT、Z.ai / BigModel 的账号登录凭据 | 不写入 | 链接只写登录方式,接收方停在登录一步,自行登录 | +| 200K 以上的分档价格;含有链接不接受的字符的值 | 不写入 | 对话框底部的「不包含」中注明 | -链接超过 8192 个字符时(通常因为价目或规格较多),链接形式不可用,对话框提示改用配置包。「导出为配置包…」打开导出对话框,并预先勾选这个上游;口令加密可选,缺省不加密。接收方通过「导入配置…」导入,配置包也能带上上游所用的出站代理等链接不能设置的内容,见[同步与备份](/zh-CN/docs/lite/sync-and-backup#导出配置)。 +链接超过 8192 个字符时(通常因为价目或规格较多),链接形式不可用,对话框写明链接的长度,提示改用配置包;上游无法写成导入链接时同样如此。「导出为配置包…」打开导出对话框,并预先勾选这个上游;口令加密可选,缺省不加密。接收方通过「导入配置…」导入,配置包也能带上上游所用的出站代理等链接不能设置的内容,见[同步与备份](/zh-CN/docs/lite/sync-and-backup#导出配置)。 链接的生成与解析遵循本页的同一套规则:生成的链接在同一版本的应用中打开时,「新建上游」对话框中的设置与原上游一致,上表中不写入的内容除外。 diff --git a/src/content/docs-lite/zh-CN/sync-and-backup.md b/src/content/docs-lite/zh-CN/sync-and-backup.md index 385af4c..4c35fdc 100644 --- a/src/content/docs-lite/zh-CN/sync-and-backup.md +++ b/src/content/docs-lite/zh-CN/sync-and-backup.md @@ -2,6 +2,8 @@ 换电脑或重装之后,ThinkWatch Lite 的配置可以从一个文件整体导入,不必逐项重新填写。多台电脑与服务器上的 core 也可以经自选的同步位置共用一份配置:在一台设备上新增上游或更换密钥,其他设备随之更新。每次配置变更都会留下一个可以恢复的版本。 +> 本页的功能需要 ThinkWatch Lite 2026.10.25 或更高版本。连接远程 core 时,服务器上的 core 须为 0.78.0 或更高版本。 + 本页说明配置包、备份、导出与导入、开启同步及各种同步位置的配置方法、加密、冲突、恢复,以及不随导出与同步带走的内容。单个上游的分享见[导入链接](/zh-CN/docs/lite/import-links#由-lite-生成)。 这些功能集中在「设置 › 同步与备份」,分为「同步」与「备份」两部分。连接远程 core 时,这一节归在服务器的配置之下,管理的是服务器上的那份配置,见[连接远程 core 时](#连接远程-core-时)。 @@ -24,7 +26,7 @@ | 本机 | 每次配置变更生成一个版本:界面修改、命令行、手动编辑文件、同步、导入、恢复与一键修复。内容与上一版本相同时不生成,ChatGPT 令牌续期也不生成。 | 最近 10 个版本 | | 同步位置 | 开启同步后,各设备上传的版本 | 最新与上一个版本 | -本机版本是完整的配置包,包含插件、ChatGPT 与 OAuth 上游的登录令牌,以及本机设置(其中的控制通道密钥以掩码保存),存放在数据目录的 `history/` 中。目录与文件只有所有者可读,不加密,与 `config.yaml` 本身的保护程度相同。这些版本取代了原先的修改记录,配置原文编辑器中的修改记录入口改为打开备份列表。 +本机版本是完整的配置包,包含插件、ChatGPT 与 OAuth 上游的登录令牌,以及本机设置(其中的控制通道密钥以掩码保存),存放在数据目录的 `history/` 中。目录与文件只有所有者可读,不加密,与 `config.yaml` 本身的保护程度相同。这些版本取代了原先的版本历史,配置原文编辑器中的「备份」同样打开这个列表。升级后第一次保留备份时,旧的版本历史文件随之删除。 「备份」部分按时间倒序列出各个版本:时间、来源(如「界面修改」「导入」「手动编辑文件」「同步自 MacBook Air」)、存放位置(本机、同步位置)、内容(如「7 个上游,2 个插件」)与生成它的 Core 版本,正在使用的版本标为「当前」。同一版本同时存在于本机与同步位置时显示为一行,两处一并标出。其余版本各有「恢复…」与「另存为…」:前者见[恢复](#恢复),后者把该版本保存为配置包,可以选择以口令加密。 @@ -35,17 +37,17 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 导出有三个入口: - 「备份」部分的「导出配置…」,缺省导出全部配置; -- 上游页多选上游之后,工具条上的「导出所选…」; +- 上游页多选上游之后,工具条上的「导出所选 N 个上游…」; - 上游分享对话框中的「导出为配置包…」。 三者打开同一个导出对话框;从上游页进入时,范围缺省为「选择项目」,并预先勾选对应的上游。 | 项目 | 说明 | |---|---| -| 范围 | 「全部配置」或「选择项目」。选择项目时,按类别列出上游、出站代理、网关密钥、别名、分组、路由、价目表、安全规则、插件与全局设置。所选项目引用的其他项目自动勾选并标为「随附」,例如路由引用的上游、上游引用的出站代理。 | +| 范围 | 「全部配置」或「选择项目」。选择项目时,按类别列出上游、出站代理、网关密钥、别名、策略组、路由、价目表、安全规则、插件与全局设置。所选项目引用的其他项目自动勾选并标为「随附」,例如路由引用的上游、上游引用的出站代理。 | | 包含密钥 | 缺省开启。包括上游密钥、请求头的值、AWS 凭据、OAuth 的 Client Secret、代理密码、网关密钥,以及 Z.ai / BigModel 账号登录所得的密钥。写成 `${变量名}` 的值原样导出,不展开。 | | 口令加密 | 缺省关闭。开启后须输入两次口令,至少 8 个字符。口令不保存,导入时须输入同一口令。 | -| 不随文件导出 | 列出这次被剔除的内容:ChatGPT 账号与使用 OAuth 刷新令牌的上游的登录(例如「ChatGPT 登录(1 个上游),导入后需重新登录」)、监听、远程控制与同步设置,以及请求记录。 | +| 不随文件导出 | 列出这次被剔除的内容,例如「ChatGPT 账号与 OAuth 的登录凭据(1 个上游),导入后需重新登录或填写。」「监听、远程控制与同步设置。」「请求记录。」关闭「包含密钥」时还列出上游密钥、网关密钥与代理密码。 | 包含密钥且不加密时,对话框底部提示「文件包含明文密钥,请妥善保管。」 @@ -65,20 +67,22 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 | 相同 | 同名且内容一致 | 不处理,只计入统计 | | 冲突 | 同名而内容不同 | 必须选择「覆盖」「改名导入」或「跳过」,没有缺省值 | | 冲突(全局设置) | 本机的该节已经修改,且与导入的值不同 | 必须选择「覆盖」或「跳过」 | -| 缺少密钥 | 配置包不含密钥;或者是使用 OAuth 刷新令牌的上游,而本机没有令牌端点相同的同名上游 | 导入为停用状态,上游行显示「缺少密钥」标签,补填密钥(OAuth 上游为 Refresh Token)后即可启用 | +| 缺少密钥 | 配置包不含密钥,且本机没有目的地相同的同名项(见下文);或者是使用 OAuth 刷新令牌的上游,而本机没有令牌端点相同的同名上游 | 导入为停用状态,上游行显示「缺少密钥」标签,补填密钥(OAuth 上游为 Refresh Token)后即可启用 | | 需要登录 | ChatGPT 上游,而本机的同名上游不是 ChatGPT 账号 | 导入为停用状态,上游行提供「登录…」 | | 插件 | 插件脚本 | 导入为停用状态;可改写工具调用的插件须在启用时批准 | -| 错误 | 引用了本机不存在的 AWS profile、出站代理或上游;字段不合法;网关密钥的值与另一把现有密钥相同 | 不可导入,注明原因 | +| 错误 | 引用了本机不存在的 AWS profile、出站代理或上游;字段不合法,例如上游地址含反斜杠,或在主机前带有用户名与密码;网关密钥的值与另一把现有密钥相同 | 不可导入,注明原因 | 所有冲突都选定处理方式之前,「导入」不可用。列表上方的「全部覆盖」「全部改名导入」「全部跳过」一次处理全部冲突。 导入按以下规则进行: -- 按名称逐项加入上游、出站代理、网关密钥、别名、分组、路由、价目表、自定义安全规则与插件。当前配置中有、配置包中没有的项保持不变,导入不删除任何内容。以一份配置整体替换当前配置属于恢复,见[恢复](#恢复)。 +- 按名称逐项加入上游、出站代理、网关密钥、别名、策略组、路由、价目表、自定义安全规则与插件。当前配置中有、配置包中没有的项保持不变,导入不删除任何内容。以一份配置整体替换当前配置属于恢复,见[恢复](#恢复)。 - 全局设置(故障转移、各项防护的状态、默认路由、默认密钥、辅助请求、价目表设置)每一节作为一项:本机该节仍为缺省值时直接采用导入的值,否则与导入的值不同即构成冲突。 -- 「改名导入」的名称缺省为原名称加序号,如「公司中转 (2)」,可以在行内修改,但不能与现有名称重复。配置包中引用该项的路由、别名与分组随之改用新名称。 +- 「改名导入」的名称缺省为原名称加序号,如「公司中转 (2)」,可以在行内修改,但不能与现有名称重复。配置包中引用该项的路由、别名与策略组随之改用新名称。 - 「覆盖」以导入的内容替换同名项,本机引用该项的路由与别名不变。覆盖本机同名的 ChatGPT 上游,或令牌端点相同的 OAuth 上游时,保留本机的登录令牌。 -- 路由、别名或分组引用了被跳过的上游时,一并标为错误,并注明缺少的上游;改名导入的上游不在此列。 +- 配置包不含密钥时,覆盖只在目的地不变时沿用本机的密钥:协议、主机与端口,AWS 区域,OAuth 令牌端点,以及所经的出站代理都相同(覆盖出站代理时比较代理的地址)。目的地不同时,该行注明「地址与本机不同,不沿用本机密钥」,上游导入为停用状态,须填写密钥。 +- 「改名导入」不沿用本机的密钥、代理密码与登录:改名导入的上游为停用状态,须填写密钥或重新登录;改名导入的插件为停用状态。 +- 路由、别名或策略组引用了被跳过的上游时,一并标为错误,并注明缺少的上游;改名导入的上游不在此列。 - 配置包不含密钥时,导入的网关密钥重新生成。 - 导入作为一个版本记入备份,因此可以恢复到导入之前的配置。预览之后配置又被修改时,导入会提示重新预览。 @@ -92,7 +96,7 @@ ThinkWatch Lite 不做定时备份,也不提供单独的手动备份。需要 | WebDAV | 坚果云、Nextcloud、群晖 NAS、InfiniCloud 等 | 地址、用户名、密码(或应用密码) | | S3 兼容存储 | Amazon S3、Cloudflare R2、阿里云 OSS、腾讯云 COS、MinIO 等 | 端点、区域、存储桶、前缀、Access Key ID、Secret Access Key | -WebDAV 与 S3 兼容存储的请求可以直连(缺省)、经系统代理,或经某个出站代理发出。两者的 `http://` 地址都只接受本机与内网地址,规则与[导入链接](/zh-CN/docs/lite/import-links#连接)相同;其他地址须使用 `https://`,证书一律校验。请求不跟随重定向,以免同步位置的密码被带到别的主机,因此须填写最终的地址。 +WebDAV 与 S3 兼容存储的请求可以直连(缺省)、经系统代理,或经某个出站代理发出,在「代理」中选择。两者的 `http://` 地址都只接受本机与内网地址,规则与[导入链接](/zh-CN/docs/lite/import-links#连接)相同,而且只能直连,以免密码以明文经过代理;其他地址须使用 `https://`,证书一律校验。请求不跟随重定向,以免同步位置的密码被带到别的主机,因此须填写最终的地址。 未开启同步时,「同步」部分只有「开启同步…」与一句说明:「多台设备共用一份配置,并在同步位置保存最新与上一个版本。」开启同步分为三步: @@ -100,9 +104,11 @@ WebDAV 与 S3 兼容存储的请求可以直连(缺省)、经系统代理, 2. **加密。**新建同步时选择加密(缺省)或不加密,加密时输入两次同步口令,至少 12 个字符。加入已有的同步时输入一次口令,并显示已在同步的设备及其最后同步时间。口令错误时提示「同步口令不正确」。 3. **合并。**只在加入已有的同步、且本机已有配置时出现。按[导入](#导入配置)的规则逐项预览,冲突项逐项选择覆盖、改名或跳过;也可以选择「使用同步数据替换本机配置」。 -本机配置仍为初始配置(新安装后未修改)时不出现第 3 步,直接使用同步数据。新安装的应用在上游页的空状态中提供「加入同步…」,直接打开上述步骤。加入之前的本机配置保留在备份中。本设备名称缺省为计算机名称,可以修改。 +合并时,各设备安装时自动生成的默认网关密钥不需要选择:采用同步数据的值,本机已接管的客户端自动改用新值。 + +本机配置仍为初始配置(新安装后未修改)时不出现第 3 步,直接使用同步数据。两台设备同时在一个空的同步位置上开启同步时,后完成的一台提示重新测试连接,再以加入的方式继续。新安装的应用在上游页的空状态中提供「加入同步…」,直接打开上述步骤。加入之前的本机配置保留在备份中。本设备名称缺省为计算机名称,可以修改。 -开启之后,「同步」部分显示状态(如「已同步 · 2 分钟前」)、同步位置及其连接参数、出站代理、加密、不同步的上游与本设备名称,以及参与同步的设备列表:每台设备的名称、最后同步时间、Core 版本与状态。「立即同步」立即检查一次。同步设置写入 `config.yaml`,修改后选择「保存」生效。 +开启之后,「同步」部分显示状态(如「已同步 · 2 分钟前」)、同步位置及其连接参数、代理、加密、不同步的上游与本设备名称,以及参与同步的设备列表:每台设备的名称、最后同步时间、Core 版本与状态。「立即同步」立即检查一次。同步设置写入 `config.yaml`,修改后选择「保存」生效。修改连接参数只用于同一个同步位置,例如更换密码;改用另一个同步位置须先停止同步,再重新开启。 ### 同步时机 @@ -157,7 +163,7 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 ### S3 兼容存储的寻址方式 -寻址方式由端点自动决定,无需设置:端点的主机以 `amazonaws.com`、`aliyuncs.com` 或 `myqcloud.com` 结尾时,存储桶写在主机名中(虚拟主机方式),即 Amazon S3、阿里云 OSS 与腾讯云 COS;其他端点,例如 Cloudflare R2、MinIO 或一个 IP 地址,存储桶写在路径中(路径方式)。 +寻址方式由端点自动决定,无需设置:端点的主机以 `amazonaws.com`、`aliyuncs.com` 或 `myqcloud.com` 结尾时,存储桶写在主机名中(虚拟主机方式),即 Amazon S3、阿里云 OSS 与腾讯云 COS;其他端点,例如 Cloudflare R2、MinIO 或一个 IP 地址,存储桶写在路径中(路径方式)。存储桶名称只能使用小写字母、数字、点与连字符;前缀是存储桶中的一个文件夹,不能以 `/` 开头,也不能含 `.` 或 `..` 段。 ### Cloudflare R2 @@ -213,20 +219,21 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 - 是否加密在开启同步时选择,缺省为加密,同一同步位置上的所有设备采用同一设置。每台设备加入时输入一次同步口令。口令与同步位置的凭据保存在本机 `config.yaml` 的 `sync` 节中,与其他密钥的保存方式相同,不随导出与同步带出。 - 加密时,第一台设备生成一把随机密钥,以同步口令加密后作为 `keyring` 存放在同步位置;各个版本与设备记录都以这把密钥加密(age 格式)。同步位置的服务商只能看到文件名(版本序号与设备标识的前 8 位)、文件大小与修改时间。认证加密同时保证版本未被篡改,没有口令的人无法写入有效的版本。 - 不加密时,版本是普通的配置包:同步位置的服务商可以读取配置及其中的密钥,任何对同步位置有写入权限的人都可以写入版本、改动配置。开启同步时选择「不加密」,会显示这一提示。为限制篡改的影响,不加密时经同步新增或脚本有变化的插件在本设备为停用状态,须在本设备上启用;上游地址经同步改变时,上游行以「地址经同步变更」标签提示一次。不加密的版本带有 SHA-256 校验和,只用于发现传输中的损坏。 -- 之后开启加密:设置同步口令后,最新版本重新以加密形式上传,未加密的版本随之删除,其他设备在下次同步时须输入口令。关闭加密须输入当前口令,随后重新上传未加密的版本。 +- 之后开启加密:设置同步口令后,最新版本重新以加密形式上传,未加密的版本随之删除,其他设备在下次同步时须输入口令。关闭加密须输入当前口令,随后重新上传未加密的版本;各设备保存的同步口令保留,用于核对同步密钥。 +- 加密的同步位置不接受未加密的版本,也不会自行关闭加密:同步密钥(`keyring`)缺失,或被替换为较早的版本时,同步暂停,而不是改为不加密,见[同步状态](#同步状态)。 - 「更改同步口令…」只重新加密 `keyring`,已有版本不变;其他设备在下次同步时显示「同步口令已在其他设备上更改」,输入新口令后继续同步。 -- 同步口令遗失、且已没有设备仍在同步时,同步位置上的内容无法解密,只能停止同步后重新开启。各设备本机的配置与备份不受影响。 +- 同步口令遗失、且已没有设备仍在同步时,同步位置上的内容无法解密,只能停止同步后重新开启。每台设备在本机保存一份同步密钥,同步位置上的同步密钥丢失时,可以用它放回。各设备本机的配置与备份不受影响。 ## 同步的内容 除本机设置、ChatGPT 与 OAuth 上游的登录令牌,以及请求记录等数据外,配置全部同步,包括密钥。完整的对照见[不导出、不同步的内容](#不导出不同步的内容)。 -- **ChatGPT 上游**:在新设备上出现时为停用状态,上游行显示「需要登录」。登录令牌与登录所得的账号标识只保存在各自的设备上,不参与比较;登录、续期与退出都不触发同步。各设备可以登录不同的 ChatGPT 账号。 -- **使用 OAuth 刷新令牌的上游**:与 ChatGPT 上游一样,每台设备分别登录。令牌端点、Client ID 与 Client Secret 照常同步;Refresh Token 与 Access Token 只保存在各自的设备上,令牌刷新不触发同步。在还没有令牌的设备上,须填入这台设备自己的 Refresh Token 后才能使用。这是因为许多 OAuth 服务每次刷新都换发新的 Refresh Token 并作废旧的,同一个令牌在两台设备上使用会相互作废。 -- **不同步的上游**:在同步设置的「不同步的上游」中选择,属于本机设置。所选上游不上传,也不会被其他设备的同名上游覆盖,适用于地址在本机的 Ollama 等。引用这些上游的路由、别名与分组随之不同步,在同步设置中列于「随之不同步」之后。开启同步时,这些上游的行显示「不同步」标签。 +- **ChatGPT 上游**:在新设备上出现时为停用状态,上游行显示「需要登录」,在这台设备上登录之前保持停用;这只影响这台设备,不会让其他设备上的这个上游停用。登录令牌与登录所得的账号标识只保存在各自的设备上,不参与比较;登录、续期与退出都不触发同步。各设备可以登录不同的 ChatGPT 账号。 +- **使用 OAuth 刷新令牌的上游**:与 ChatGPT 上游一样,每台设备分别登录。令牌端点、Client ID 与 Client Secret 照常同步;Refresh Token 与 Access Token 只保存在各自的设备上,令牌刷新不触发同步。在还没有令牌的设备上,这个上游保持停用,填入这台设备自己的 Refresh Token 后才能使用;其他设备不受影响。这是因为许多 OAuth 服务每次刷新都换发新的 Refresh Token 并作废旧的,同一个令牌在两台设备上使用会相互作废。 +- **不同步的上游**:在同步设置的「不同步的上游」中选择,属于本机设置。所选上游不上传,也不会被其他设备的同名上游覆盖,适用于地址在本机的 Ollama 等。引用这些上游的路由、别名与策略组随之不同步,在同步设置中列于「随之不同步」之后。开启同步时,这些上游的行显示「不同步」标签。 - **插件**:随同步分发,启用状态在各设备之间保持一致;可改写工具调用的插件须在每台设备上分别批准后才运行。Core 自带的默认插件,脚本由各设备的 Core 提供并随 Core 升级,不同步,启用状态与设置照常同步。各设备的 Core 版本不同时,默认插件的版本差异不引起同步。 - **环境变量**:写成 `${变量名}` 的值原样同步。某台设备没有设置这个变量时,该上游在这台设备上的请求失败,上游行显示原因,不影响其他配置。 -- **Lite 的通用偏好**:语言、外观、菜单栏显示、通知与检查更新随同步保持一致,其他设备上的改动即时生效。 +- **Lite 的通用偏好**:语言、外观、菜单栏显示、通知与检查更新随同步保持一致,其他设备上的改动即时生效。同步数据中的通用偏好超过 64 KiB 时,本设备保留自己的通用偏好,其余内容照常同步。 ## 冲突 @@ -237,9 +244,9 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 | 对象 | 合并单位 | |---|---| | 上游 | 连接(服务类型、地址、协议、认证方式、密钥、请求头、出站代理、AWS 设置);模型(模型列表、只用这些模型、手动添加的模型、规格);计费(计费方式、价目、价格倍率);其他(并发上限、转发客户端身份、余额来源、停用状态) | -| 出站代理、网关密钥、别名、分组、路由、价目表、自定义安全规则 | 整个对象 | +| 出站代理、网关密钥、别名、策略组、路由、价目表、自定义安全规则 | 整个对象 | | 插件 | 脚本;设置;启用状态 | -| 路由顺序 | 一个单位 | +| 上游顺序、路由顺序、插件顺序 | 各一个单位 | | 全局设置、Lite 的通用偏好 | 每个字段 | 两台设备修改同一对象的不同合并单位时自动合并,例如一台更换了上游的密钥,另一台修改了它的价目。只有同一合并单位在两边被改为不同的内容,或一边删除而另一边修改时,才构成冲突。重命名按删除原名称、新增新名称处理。合并的结果须通过配置校验,例如路由引用的上游必须存在;校验不通过的对象按冲突处理。 @@ -250,8 +257,8 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 - 冲突不阻断同步:其余内容照常合并与上传,冲突的对象在各设备上沿用各自的内容。 - 冲突记录随版本上传,参与冲突的设备显示同一组冲突。在任一台设备上处理之后,结果作为新版本上传,其他设备随之清除这些冲突,不必重复处理。 -- 「同步」部分与上游页顶部显示黄色提示,例如「同步有 2 项冲突」,点击打开「同步冲突」对话框。提示可以忽略,冲突保留到处理为止。 -- 对话框逐项列出对象、双方的修改时间与设备名称以及差异的字段,点击对象名称查看差异。每一项在「采用」一列中选择本机、另一台设备,或「两者都保留」:本机的内容保留原名称,另一方的内容以「名称 (设备名称)」新增,名称可以修改。全局设置、路由顺序与 Lite 的通用偏好不提供「两者都保留」。「稍后处理」关闭对话框而不做改动,「应用」写入所选的结果。 +- 「同步」部分与上游页顶部显示黄色提示,例如「同步有 2 项冲突」,其中的「处理冲突…」打开「同步冲突」对话框。提示可以忽略,冲突保留到处理为止。 +- 对话框逐项列出对象、双方的修改时间与设备名称以及差异的字段,点击对象名称查看差异。每一项在「采用」一列中选择本机、另一台设备,或「两者都保留」:本机的内容保留原名称,另一方的内容以「名称 (设备名称)」新增,名称可以修改。全局设置、各种顺序与 Lite 的通用偏好,以及一方已删除的对象,不提供「两者都保留」。「稍后处理」关闭对话框而不做改动;「应用」写入已选择的各项,未选择的留待下次处理。 ## 同步状态 @@ -259,28 +266,33 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 |---|---|---| | 已同步 | 设置中显示「已同步 · 2 分钟前」,其他页面不提示 | — | | 同步中 | 设置中显示「正在同步」 | — | -| 冲突 | 黄色提示,如「同步有 2 项冲突」 | 处理冲突 | +| 冲突 | 黄色提示,如「同步有 2 项冲突」;设置中显示「有 2 项冲突」 | 「处理冲突…」 | | 同步位置不可用 | 连续 2 小时失败后,显示黄色提示「无法连接同步位置」及原因 | 恢复后自动继续,本机的修改随后上传 | | 认证失败 | 黄色提示「同步位置拒绝访问」 | 修改连接参数 | -| 同步口令已更改 | 黄色提示「同步口令已在其他设备上更改」 | 输入新口令 | -| 版本过新 | 黄色提示「其他设备使用较新版本,更新 ThinkWatch Lite 后继续同步」;其他设备的设备列表中,这台设备显示「已暂停:需更新」 | 更新 ThinkWatch Lite | +| 同步口令已更改 | 黄色提示「同步口令已在其他设备上更改」 | 「输入口令…」,输入新口令 | +| 版本过新 | 黄色提示「同步已暂停」:「其他设备使用较新版本,更新 ThinkWatch Lite 后继续同步。」其他设备的设备列表中,这台设备显示「已暂停:需更新」 | 「检查更新」,更新 ThinkWatch Lite | +| 同步密钥不可用 | 黄色提示「同步位置上的同步密钥不可用」:同步密钥缺失、被替换为较早的版本或无法读取,同步暂停 | 「重新上传本机配置」(同时放回本设备保存的同步密钥),或「停止同步…」 | +| 同步位置为空 | 黄色提示「同步位置为空」:其他设备可能删除了同步数据,或同步位置已不是原来的位置,同步暂停。同步位置读来为空时,先再检查几次才作此判断 | 「重新上传本机配置」,或「停止同步…」 | +| 同步设置无法使用 | 黄色提示「同步设置无法使用」,例如所选的出站代理已不存在 | 修改同步位置的连接参数或代理 | +| 安全模式 | 状态显示「已暂停:安全模式」 | 配置恢复正常后自动继续 | -正常运转时不发送系统通知。需要处理的状态按「设置 › 通用」的提醒设置发送一次系统通知,黄色提示都可以忽略。 +以上任何一种状态都不会自动停止同步,是否停止由用户决定。正常运转时不发送系统通知;需要处理的状态按「设置 › 通用」的提醒设置发送一次系统通知,黄色提示都可以忽略。同步位置上有无法使用的文件时,状态旁注明跳过了几个。 同步位置上的最新版本由较新的 Core 生成、本机无法读取时,本机暂停同步,既不下载也不上传。暂停期间本机的修改只保存在本机,更新之后再按合并规则合并,较旧的 Core 因此不会覆盖较新 Core 写入的字段。远程 core 需要在服务器上单独更新,设备列表显示每台设备的 Core 版本。 -180 天未更新的设备记录自动移除。安全模式下不自动同步。 +180 天未更新的设备记录自动移除。 ## 停止同步 「停止同步…」提供两种方式: - **停止同步**:本机保留当前配置,删除本机的同步设置,并从同步位置移除本设备的设备记录。其他设备不受影响。 -- **停止同步并删除同步数据**:须再次确认,删除同步位置上的全部版本、设备记录与 `keyring`。其他设备随后提示同步位置为空,并停止同步。 +- **停止同步并删除同步数据…**:须再次确认,删除同步位置上的全部版本、设备记录与 `keyring`,无法撤销;本机配置与本机备份不受影响。其他设备随后提示同步位置为空,各自可以重新上传本机配置,或停止同步。 ## 恢复 - 「备份」列表中,每个版本的「恢复…」打开恢复预览。预览按项目列出该版本与当前配置之间的差异(新增、删除、修改),缺省全部勾选,即以该版本整体替换当前配置;取消勾选的项目保持当前内容。只需找回一个已删除的上游时,只勾选这一项即可。 +- 勾选的项目引用了未勾选的项目时,后者随所选项目一并恢复;要删除的项目仍被保留的项目引用时,标为「保留:仍被保留的项目引用」,不删除。预览与写入按同一方案进行。 - 本机设置保留当前的值。同名的 ChatGPT 上游,以及令牌端点相同的 OAuth 上游,保留当前的登录令牌,不写回备份中的旧令牌,因为旧令牌可能已在刷新后作废。 - 恢复本身作为一个新版本记入备份,因此可以撤销。 - 开启同步时,恢复的结果作为新版本上传,其他设备随之更新,预览对话框底部注明,例如「恢复后的配置将同步到其他 2 台设备」。不同步的上游不受影响。 @@ -289,6 +301,8 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 配置无法读取而进入安全模式时,出错页在「一键修复」旁提供「从备份恢复…」:列出本机的版本,同步位置可用时也列出同步位置上的版本,选择后直接进入恢复预览。配置完全无法读取时,恢复取回整个版本,只保留当前的控制通道密钥。升级本身不改写配置,因此不单独生成版本;升级后无法读取的配置就是最新的版本,一键修复之前的内容同样保留在备份中。 +从 2026.10.25 起,上游地址不能含反斜杠,也不能在主机前带有用户名或密码。含有这种地址的配置升级后进入安全模式;备份中的地址与此相同,应修改配置文件,把凭据移到密钥或请求头中。 + ## 与已接管客户端的关系 本机已接管的客户端在各自的配置文件中写有网关密钥。同步、恢复与导入(覆盖)改动网关密钥时按下表处理,客户端不会因此失效: @@ -296,7 +310,8 @@ ThinkWatch 强制校验 TLS 证书,DSM 自带的自签名证书无法通过校 | 情形 | 处理 | |---|---| | 客户端所用的网关密钥的值被替换 | 与在密钥页更换密钥时一样,自动改写这些客户端的配置 | -| 客户端所用的网关密钥将被删除 | 同步:保留这把密钥,作为冲突列出,并注明如「本机 2 个客户端正在使用」。恢复:预览中该项标为「保留」,不删除 | +| 客户端所用的网关密钥将被删除 | 同步:保留这把密钥,作为冲突列出,并注明如「本机 2 个客户端正在使用」。恢复:预览中该项标为「保留:本机客户端正在使用」,不删除 | +| 加入同步时,两边的默认网关密钥不同 | 采用同步数据的值,本机已接管的客户端自动改用新值 | | 加入同步时选择「使用同步数据替换本机配置」 | 本机客户端正在使用的密钥并入同步数据,随后同步到其他设备;加入的步骤中会注明 | 连接远程 core 时,由连接着它的 Lite 在收到变更时做同样的处理。 @@ -323,6 +338,7 @@ twcore backup restore <编号> twcore sync status twcore sync now twcore sync enable +twcore sync republish twcore sync disable --purge ``` @@ -330,7 +346,7 @@ twcore sync disable --purge - `import` 加入本机没有的项目,与本机不同的项目逐项询问;`--on-conflict overwrite|rename|skip` 对所有这样的项目一并作答,`--dry-run` 只显示导入会做什么,不写入。加密的配置包同样询问口令或读取 `TWCORE_PASSPHRASE`;较旧版本导出的配置包先列出自动修复之处。 - `backup list` 按时间倒序列出版本及其编号;`backup restore` 恢复整个版本,`--dry-run` 只列出会改变的内容。恢复保留本机设置、ChatGPT 登录与本机客户端正在使用的网关密钥。 - `export`、`import` 与 `backup` 直接读写数据目录中的文件,core 未运行时同样可用,配置损坏、core 无法启动时也能恢复;正在运行的 core 在一秒内读入改动。 -- `sync` 经控制通道与正在运行的 core 通信:`status` 显示状态、同步位置与设备,`now` 立即同步一次,`enable` 以交互方式填写同步位置与口令,`disable` 停止同步、本机保留当前配置,加 `--purge` 同时删除同步位置上的全部版本、设备记录与 `keyring`。 +- `sync` 经控制通道与正在运行的 core 通信:`status` 显示状态、同步位置与设备,`now` 立即同步一次,`enable` 以交互方式填写同步位置与口令,`republish` 在同步位置被清空或丢失同步密钥之后重新上传本机配置(并放回本设备保存的同步密钥),`disable` 停止同步、本机保留当前配置,加 `--purge` 同时删除同步位置上的全部版本、设备记录与 `keyring`。 用安装脚本安装的服务器上,命令以 `sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch` 开头,与[连接远程 core](/zh-CN/docs/lite/remote-core) 中的命令相同。各命令的完整参数见 `twcore <命令> --help`。 @@ -338,7 +354,7 @@ twcore sync disable --purge | 内容 | 导出 | 同步 | 本机备份 | 说明 | |---|---|---|---|---| -| 上游、出站代理、别名、分组、路由、默认路由、价目表、安全规则、故障转移、辅助请求 | 包含 | 包含 | 包含 | — | +| 上游、出站代理、别名、策略组、路由、默认路由、价目表、安全规则、故障转移、辅助请求 | 包含 | 包含 | 包含 | — | | 插件脚本与设置 | 包含 | 包含 | 包含 | 导入后为停用状态;同步见[同步的内容](#同步的内容) | | 上游密钥、请求头的值、AWS 凭据、OAuth 的 Client Secret、代理密码、网关密钥、Z.ai / BigModel 登录所得的密钥 | 缺省包含,可以关闭 | 包含 | 包含 | `${变量名}` 原样保留,不展开。导出时关闭「包含密钥」的,导入时须补填上游密钥,网关密钥重新生成 | | ChatGPT 登录令牌;OAuth 上游的 Refresh Token 与 Access Token | 不包含 | 不包含 | 包含 | 同一个刷新令牌在两台设备上使用会相互作废。上游本身照常导出与同步,每台设备分别登录 |