fix(apps): harden Tailscale health reuse, settings merge, and key cleanup

QA review of the Tailscale connector found five defects and a doc claim:

- Catalog refresh reused a stored probe summary even after a later health
  check failed, reporting the connection healthy without contacting
  Tailscale. Reuse now requires healthStatus ok, a healthCheckedAt no older
  than the summary, and a probe fingerprint (tailnet, agent tag, secret
  versions) that still matches the connection.
- The health check wrote the whole in-memory config back, restoring a
  tailnet or tag an operator saved during the probe. It now merges only
  config.tailscale with a jsonb merge fenced on methodConfig and
  credentialSecretRefs being unchanged, and reports
  tailscale_connection_changed (409, degraded) when the fence fails.
- A create-key response with a usable id but invalid other fields skipped
  deletion; the key is now deleted before the shape error is thrown.
- A cleanup failure after a tag validation failure was hidden behind the
  tag error; the cleanup failure is now primary and the superseded error
  is kept in details.secondary and appended to the message.
- A response stream that failed mid-body threw a raw TypeError; it is now
  normalized to tailscale_request_failed.
- The catalog and doc no longer claim agents get network reach from this
  connection.

Tests cover each case in the API unit suite and the embedded-Postgres
lifecycle suite.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
devinfoleyandClaude Fable 5.1 committed 2026-10-09 05:02:48 +00:00
1 parent 47bbe1aba4
commit 2c5ec9ce10
8 files changed
+395 -69

No files matched your search

+3 -3
View File
@@ -922,11 +922,11 @@ const apps = [
}),
{ docsUrl: "https://docs.browser-use.com/cloud/api-v4-overview" },
],
["tailscale", "Tailscale", "Reach tailnet machines and private services from agent environments.", "developer", "tailscale.com", ["https://login.tailscale.com/*"],
["tailscale", "Tailscale", "Hold a Tailscale OAuth client so Paperclip can verify tailnet access and mint tagged auth keys server-side.", "developer", "tailscale.com", ["https://login.tailscale.com/*"],
method("oauth-client", "rest_api", "api_key", { serverUrl: "https://api.tailscale.com/api/v2" }, "S3",
"Create an OAuth client in [Tailscale trust credentials](https://login.tailscale.com/admin/settings/oauth) with the `auth_keys` scope (tags `tag:paperclip-agent` and `tag:paperclip-control`) and the `devices:core` scope, then paste its client ID and secret. Paperclip verifies the client server-side by listing devices and minting one short-lived ephemeral test key that it deletes immediately. The client never leaves the server.", {
label: "Tailscale OAuth client",
whenToUse: "Connect the tailnet whose machines and services agents should reach.",
whenToUse: "Connect the tailnet that later Paperclip features will use for agent machines and sandboxes.",
grantKinds: ["organization"],
credentialFields: [
{ ...field("oauthClientId", "OAuth client ID", "k1234567CNTRL"), helperMd: "Copy the client ID shown after you create the OAuth client." },
@@ -941,7 +941,7 @@ const apps = [
{ key: "agentTag", label: "Agent tag", type: "text", required: true, advanced: true, defaultValue: "tag:paperclip-agent", placeholder: "tag:paperclip-agent", helperMd: "Tag assigned to machines and sandboxes that join the tailnet for agents. The OAuth client must be allowed to assign it.", validation: { pattern: "^tag:[A-Za-z0-9_-]+$", maxLength: 128 } },
],
consoleLinks: { keys: "https://login.tailscale.com/admin/settings/oauth", docs: "https://tailscale.com/kb/1215/oauth-clients" },
warnings: ["Agents get network reach through this connection; they do not get tools that change the tailnet in this version."],
warnings: ["This version only verifies the OAuth client. Agents get no tools and no tailnet access from this connection yet; machine and sandbox access are separate, later features."],
}),
{ docsUrl: "https://tailscale.com/kb/1215/oauth-clients" },
],