Files
NetMesh/docs/plugin-platform/sync-providers.md
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

3.9 KiB

Sync providers

Netcatty cloud sync providers are dynamic and namespaced. Built-in providers (github, google, onedrive, webdav, s3) stay compatible; plugins register additional IDs under their plugin namespace with kind: "sync" and permission provider.sync.

Boundary

Plugins implement encrypted object storage only:

  • connect / disconnect / getAccount
  • getCapabilities (revisions, conditionalWrites, atomicReplacement, size limits)
  • readObject / writeObject / deleteObject

Netcatty owns encryption, the master key, CRDT merge, migrations, protection snapshots, conflict handling, and read-merge-write-verify. Plugin providers never receive the vault master key or plaintext sync payloads.

Secrets

Only non-secret configuration marked for sync enters cloud payloads. Plugin connect secrets (password, token, secret, apiKey, accessToken) are stripped from configuration, stored in the OS-backed plugin secret store, and passed to SyncConnectPayload.credential as opaque { kind: "secret", id, key } references. Additional extracted secrets are stored under sync-credential:<field> keys so plugins can secrets.get / credentials.createLease them.

Durable reconnects persist an opaque SecretRef ({ kind, id, key }), not plaintext. The host injects Authorization only after consuming an operation-bound lease whose operationId matches network:<origin>.

Using a SecretRef from a sandbox plugin: create an operation-bound lease via credentials.createLease with operationId set to network:<origin> (same origin the request will call), then call network.request with:

{
  "url": "https://example.com/…",
  "credentialLease": { "kind": "secret-lease", "id": "…", "operationId": "network:https://example.com", "expiresAt": 0 },
  "authorization": { "scheme": "Bearer" }
}

The host consumes the lease (bound to the request origin, not a plugin-echoed id) and injects Authorization (Bearer or Basic). Plaintext never returns to the plugin. Companion credentialLeases remains available for node-only companions.

WebDAV continues to exercise the shared EncryptedObjectStorage path for configuration, proxy behavior, upload/download, and recovery. Write verification on WebDAV is performed by the adapter's pad+verify upload (not a second host byte re-read). Credentials remain field-encrypted at rest via the secure field adapter.

Streams and SyncLimits

Public SyncLimits (see plugin contract) define:

  • maxObjectBytes — hard ciphertext cap
  • inlineObjectBytes — maximum size that may travel inline on the control plane
  • key / revision length bounds

Above inlineObjectBytes, main↔plugin uses credit-window streams (STREAM_WINDOW_BYTES = 256 KiB). Renderer↔main uses structured-clone Uint8Array for inline payloads and pull/chunked IPC (sync-write-begin / sync-write-chunk / sync-write-commit, sync-read-chunk) for larger objects. Transfers are per-sender, TTL-bounded, capped, and cancelled via cancelPluginExtensionRequest(requestId) / AbortSignal.

Sidecars (non-cascade)

Missing or disabled plugins must not delete synced settings or connection baselines. Host-owned plugin_sync_sidecars carry sync:true non-secret settings and account/CRDT baselines through collect/apply with last-known and prefer-cloud merge semantics. Device-local baselines survive remote settings wipes; empty-vault upload guards ignore last-known-only evidence.

WebDAV

The production WebDAV adapter is wrapped as EncryptedObjectStorage so it shares the same encrypt→write / read→decrypt surface as plugin providers. WebDAV's native pad+verify upload already satisfies write verification and may leave trailing padding on the remote object; the shared bridge therefore skips a full byte re-read on that path (assumeVerifiedWrites) — without that flag the host compare would false-fail on padded bodies. Plugin providers keep host-owned byte compare after write.